aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore36
-rw-r--r--CMakeLists.txt35
-rw-r--r--LICENSE22
-rw-r--r--README.md35
-rw-r--r--docs/Makefile177
-rw-r--r--docs/common-patterns.rst249
-rw-r--r--docs/conf.py259
-rw-r--r--docs/contracts.rst884
-rw-r--r--docs/control-structures.rst169
-rw-r--r--docs/frequently-asked-questions.rst785
-rw-r--r--docs/index.rst66
-rw-r--r--docs/installing-solidity.rst151
-rw-r--r--docs/introduction-to-smart-contracts.rst464
-rw-r--r--docs/layout-of-source-files.rst138
-rw-r--r--docs/make.bat242
-rw-r--r--docs/miscellaneous.rst206
-rw-r--r--docs/solidity-by-example.rst530
-rw-r--r--docs/solidity-in-depth.rst19
-rw-r--r--docs/structure-of-a-contract.rst128
-rw-r--r--docs/style-guide.rst635
-rw-r--r--docs/types.rst571
-rw-r--r--docs/units-and-global-variables.rst125
-rw-r--r--docs/utils/SolidityLexer.py85
-rw-r--r--libsolidity/CMakeLists.txt21
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp59
-rw-r--r--libsolidity/analysis/ConstantEvaluator.h50
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp96
-rw-r--r--libsolidity/analysis/DeclarationContainer.h68
-rw-r--r--libsolidity/analysis/DocStringAnalyser.cpp128
-rw-r--r--libsolidity/analysis/DocStringAnalyser.h71
-rw-r--r--libsolidity/analysis/GlobalContext.cpp102
-rw-r--r--libsolidity/analysis/GlobalContext.h64
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp642
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h178
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp244
-rw-r--r--libsolidity/analysis/ReferencesResolver.h90
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp80
-rw-r--r--libsolidity/analysis/SyntaxChecker.h61
-rw-r--r--libsolidity/analysis/TypeChecker.cpp1472
-rw-r--r--libsolidity/analysis/TypeChecker.h128
-rw-r--r--libsolidity/ast/AST.cpp413
-rw-r--r--libsolidity/ast/AST.h1468
-rw-r--r--libsolidity/ast/ASTAnnotations.cpp28
-rw-r--r--libsolidity/ast/ASTAnnotations.h185
-rw-r--r--libsolidity/ast/ASTForward.h98
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp481
-rw-r--r--libsolidity/ast/ASTJsonConverter.h138
-rw-r--r--libsolidity/ast/ASTPrinter.cpp582
-rw-r--r--libsolidity/ast/ASTPrinter.h147
-rw-r--r--libsolidity/ast/ASTUtils.cpp48
-rw-r--r--libsolidity/ast/ASTUtils.h54
-rw-r--r--libsolidity/ast/ASTVisitor.h302
-rw-r--r--libsolidity/ast/AST_accept.h755
-rw-r--r--libsolidity/ast/Types.cpp2029
-rw-r--r--libsolidity/ast/Types.h1033
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp968
-rw-r--r--libsolidity/codegen/ArrayUtils.h103
-rw-r--r--libsolidity/codegen/Compiler.cpp788
-rw-r--r--libsolidity/codegen/Compiler.h137
-rw-r--r--libsolidity/codegen/CompilerContext.cpp223
-rw-r--r--libsolidity/codegen/CompilerContext.h189
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp822
-rw-r--r--libsolidity/codegen/CompilerUtils.h193
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp1551
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h139
-rw-r--r--libsolidity/codegen/LValue.cpp556
-rw-r--r--libsolidity/codegen/LValue.h224
-rw-r--r--libsolidity/formal/Why3Translator.cpp681
-rw-r--r--libsolidity/formal/Why3Translator.h134
-rw-r--r--libsolidity/grammar.txt48
-rw-r--r--libsolidity/interface/CompilerStack.cpp551
-rw-r--r--libsolidity/interface/CompilerStack.h240
-rw-r--r--libsolidity/interface/Exceptions.cpp58
-rw-r--r--libsolidity/interface/Exceptions.h103
-rw-r--r--libsolidity/interface/GasEstimator.cpp191
-rw-r--r--libsolidity/interface/GasEstimator.h85
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp248
-rw-r--r--libsolidity/interface/InterfaceHandler.h93
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp126
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.h54
-rw-r--r--libsolidity/interface/Utils.h39
-rw-r--r--libsolidity/interface/Version.cpp76
-rw-r--r--libsolidity/interface/Version.h42
-rw-r--r--libsolidity/parsing/DocStringParser.cpp141
-rw-r--r--libsolidity/parsing/DocStringParser.h70
-rw-r--r--libsolidity/parsing/Parser.cpp1282
-rw-r--r--libsolidity/parsing/Parser.h147
-rw-r--r--libsolidity/parsing/ParserBase.cpp103
-rw-r--r--libsolidity/parsing/ParserBase.h74
-rw-r--r--libsolidity/parsing/Scanner.cpp777
-rw-r--r--libsolidity/parsing/Scanner.h226
-rw-r--r--libsolidity/parsing/Token.cpp191
-rw-r--r--libsolidity/parsing/Token.h350
-rw-r--r--solc/CMakeLists.txt27
-rw-r--r--solc/CommandLineInterface.cpp860
-rw-r--r--solc/CommandLineInterface.h93
-rw-r--r--solc/jsonCompiler.cpp275
-rw-r--r--solc/main.cpp47
-rw-r--r--test/CMakeLists.txt53
-rw-r--r--test/TestHelper.cpp59
-rw-r--r--test/TestHelper.h133
-rw-r--r--test/boostTest.cpp33
-rw-r--r--test/contracts/AuctionRegistrar.cpp492
-rw-r--r--test/contracts/CMakeLists.txt5
-rw-r--r--test/contracts/FixedFeeRegistrar.cpp251
-rw-r--r--test/contracts/Wallet.cpp677
-rw-r--r--test/libsolidity/Assembly.cpp130
-rw-r--r--test/libsolidity/CMakeLists.txt5
-rw-r--r--test/libsolidity/GasMeter.cpp237
-rw-r--r--test/libsolidity/Imports.cpp149
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp633
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp6493
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp507
-rw-r--r--test/libsolidity/SolidityInterface.cpp165
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp3265
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp581
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp1181
-rw-r--r--test/libsolidity/SolidityParser.cpp1185
-rw-r--r--test/libsolidity/SolidityScanner.cpp298
-rw-r--r--test/libsolidity/SolidityTypes.cpp93
-rw-r--r--test/libsolidity/solidityExecutionFramework.h311
121 files changed, 47587 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..260be905
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Build directory
+build/
+docs/_build
+docs/utils/__pycache__
+
+# vim stuff
+*.swp
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..a02b779e
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.0.0)
+
+set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/../webthree-helpers/cmake" CACHE PATH "The the path to the cmake directory")
+list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR})
+
+# Set cmake_policies
+include(EthPolicy)
+eth_policy()
+
+# project name and version should be set after cmake_policy CMP0048
+set(PROJECT_VERSION "0.3.0")
+project(solidity VERSION ${PROJECT_VERSION})
+
+# Let's find our dependencies
+include(EthDependencies)
+
+# Figure out what compiler and system are we using
+include(EthCompilerSettings)
+
+# Include helper macros
+include(EthExecutableHelper)
+
+# Include utils
+include(EthUtils)
+
+include(EthOptions)
+configure_project(TESTS)
+
+add_subdirectory(libsolidity)
+add_subdirectory(solc)
+if (NOT EMSCRIPTEN)
+ add_subdirectory(test)
+endif()
+
+# TODO installation and packaging rules
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..20efd1b3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..217b7f23
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# The Solidity Contract-Oriented Programming Language
+[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+## Useful links
+
+To get started you can find a basic introduction to the language in the [Solidity Documentation](https://solidity.readthedocs.org).
+
+You can start using [Solidity in your browser](https://chriseth.github.io/browser-solidity/) with no need to download or compile anything. This application only supports compilation - if you want to inject it into the blockchain, you have to use a client like [Geth](https://github.com/ethereum/go-ethereum/wiki) or [AlethZero](https://github.com/ethereum/alethzero).
+
+Also check out more documentations for [Solidity ABI](https://github.com/ethereum/wiki/wiki/Solidity,-Docs-and-ABI) and a crowdfunding [example contract](https://github.com/chriseth/cpp-ethereum/wiki/Crowdfunding-example-contract-in-Solidity) written in Solidity.
+
+[Changelog](https://github.com/ethereum/wiki/wiki/Solidity-Changelog)
+
+The [Solidity Features](https://github.com/ethereum/wiki/wiki/Solidity-Features) page contains a list of already completed Solidity feature stories with explanations and demonstrations, it is being updated continuously. More technical information you can find in the [Pivotal Tracker](https://www.pivotaltracker.com/n/projects/1189488).
+
+Solidity is still under development. So please do not hesitate and open an [issue in github](https://github.com/ethereum/solidity/issues) if you encounter anything strange.
+
+## Building
+
+See the [Wiki](https://github.com/ethereum/webthree-umbrella/wiki) for build instructions, compatibility information and build tips.
+
+## How to Contribute
+
+### External Contributors
+
+I hereby place all my contributions in this codebase under an MIT
+licence, as specified [here](http://opensource.org/licenses/MIT).
+- *Name Surname* (**email@domain**)
+
+### Contribution guideline
+
+Please add yourself in the `@author` doxygen section of the file your are adding/editing
+with the same wording as the one you listed yourself in the external contributors section above,
+only replacing the word **contribution** by **file**
+
+Please read [CodingStandards.txt](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt) thoroughly before making alterations to the code base. Please do *NOT* use an editor that automatically reformats whitespace away from astylerc or the formatting guidelines as described in [CodingStandards.txt](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt).
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..3cc98f69
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Solidity.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Solidity.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Solidity"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Solidity"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst
new file mode 100644
index 00000000..f4794221
--- /dev/null
+++ b/docs/common-patterns.rst
@@ -0,0 +1,249 @@
+###############
+Common Patterns
+###############
+
+.. index:: access;restricting
+
+******************
+Restricting Access
+******************
+
+Restricting access is a common pattern for contracts.
+Note that you can never restrict any human or computer
+from reading the content of your transactions or
+your contract's state. You can make it a bit harder
+by using encryption, but if your contract is supposed
+to read the data, so will everyone else.
+
+You can restrict read access to your contract's state
+by **other contracts**. That is actually the default
+unless you declare make your state variables `public`.
+
+Furthermore, you can restrict who can make modifications
+to your contract's state or call your contract's
+functions and this is what this page is about.
+
+.. index:: function;modifier
+
+The use of **function modifiers** makes these
+restrictions highly readable.
+
+.. {% include open_link gist="fe4ef267cbdeac151b98" %}
+
+::
+
+ contract AccessRestriction {
+ // These will be assigned at the construction
+ // phase, where `msg.sender` is the account
+ // creating this contract.
+ address public owner = msg.sender;
+ uint public creationTime = now;
+
+ // Modifiers can be used to change
+ // the body of a function.
+ // If this modifier is used, it will
+ // prepend a check that only passes
+ // if the function is called from
+ // a certain address.
+ modifier onlyBy(address _account)
+ {
+ if (msg.sender != _account)
+ throw;
+ // Do not forget the "_"! It will
+ // be replaced by the actual function
+ // body when the modifier is invoked.
+ _
+ }
+
+ /// Make `_newOwner` the new owner of this
+ /// contract.
+ function changeOwner(address _newOwner)
+ onlyBy(owner)
+ {
+ owner = _newOwner;
+ }
+
+ modifier onlyAfter(uint _time) {
+ if (now < _time) throw;
+ _
+ }
+
+ /// Erase ownership information.
+ /// May only be called 6 weeks after
+ /// the contract has been created.
+ function disown()
+ onlyBy(owner)
+ onlyAfter(creationTime + 6 weeks)
+ {
+ delete owner;
+ }
+
+ // This modifier requires a certain
+ // fee being associated with a function call.
+ // If the caller sent too much, he or she is
+ // refunded, but only after the function body.
+ // This is dangerous, because if the function
+ // uses `return` explicitly, this will not be
+ // done!
+ modifier costs(uint _amount) {
+ if (msg.value < _amount)
+ throw;
+ _
+ if (msg.value > _amount)
+ msg.sender.send(_amount - msg.value);
+ }
+
+ function forceOwnerChange(address _newOwner)
+ costs(200 ether)
+ {
+ owner = _newOwner;
+ // just some example condition
+ if (uint(owner) & 0 == 1)
+ // in this case, overpaid fees will not
+ // be refunded
+ return;
+ // otherwise, refund overpaid fees
+ }
+ }
+
+A more specialised way in which access to function
+calls can be restricted will be discussed
+in the next example.
+
+.. index:: state machine
+
+*************
+State Machine
+*************
+
+Contracts often act as a state machine, which means
+that they have certain **stages** in which they behave
+differently or in which different functions can
+be called. A function call often ends a stage
+and transitions the contract into the next stage
+(especially if the contract models **interaction**).
+It is also common that some stages are automatically
+reached at a certain point in **time**.
+
+An example for this is a blind auction contract which
+starts in the stage "accepting blinded bids", then
+transitions to "revealing bids" which is ended by
+"determine auction autcome".
+
+.. index:: function;modifier
+
+Function modifiers can be used in this situation
+to model the states and guard against
+incorrect usage of the contract.
+
+Example
+=======
+
+In the following example,
+the modifier `atStage` ensures that the function can
+only be called at a certain stage.
+
+Automatic timed transitions
+are handled by the modifier `timeTransitions`, which
+should be used for all functions.
+
+.. note::
+ **Modifier Order Matters**.
+ If atStage is combined
+ with timedTransitions, make sure that you mention
+ it after the latter, so that the new stage is
+ taken into account.
+
+Finally, the modifier `transitionNext` can be used
+to automatically go to the next stage when the
+function finishes.
+
+.. note::
+ **Modifier May be Skipped**.
+ Since modifiers are applied by simply replacing
+ code and not by using a function call,
+ the code in the transitionNext modifier
+ can be skipped if the function itself uses
+ return. If you want to do that, make sure
+ to call nextStage manually from those functions.
+
+.. {% include open_link gist="0a221eaceb6d708bf271" %}
+
+::
+
+ contract StateMachine {
+ enum Stages {
+ AcceptingBlindedBids,
+ RevealBids,
+ AnotherStage,
+ AreWeDoneYet,
+ Finished
+ }
+ // This is the current stage.
+ Stages public stage = Stages.AcceptingBlindedBids;
+
+ uint public creationTime = now;
+
+ modifier atStage(Stages _stage) {
+ if (stage != _stage) throw;
+ _
+ }
+ function nextStage() internal {
+ stage = Stages(uint(stage) + 1);
+ }
+ // Perform timed transitions. Be sure to mention
+ // this modifier first, otherwise the guards
+ // will not take the new stage into account.
+ modifier timedTransitions() {
+ if (stage == Stages.AcceptingBlindedBids &&
+ now >= creationTime + 10 days)
+ nextStage();
+ if (stage == Stages.RevealBids &&
+ now >= creationTime + 12 days)
+ nextStage();
+ // The other stages transition by transaction
+ }
+
+ // Order of the modifiers matters here!
+ function bid()
+ timedTransitions
+ atStage(Stages.AcceptingBlindedBids)
+ {
+ // We will not implement that here
+ }
+ function reveal()
+ timedTransitions
+ atStage(Stages.RevealBids)
+ {
+ }
+
+ // This modifier goes to the next stage
+ // after the function is done.
+ // If you use `return` in the function,
+ // `nextStage` will not be called
+ // automatically.
+ modifier transitionNext()
+ {
+ _
+ nextStage();
+ }
+ function g()
+ timedTransitions
+ atStage(Stages.AnotherStage)
+ transitionNext
+ {
+ // If you want to use `return` here,
+ // you have to call `nextStage()` manually.
+ }
+ function h()
+ timedTransitions
+ atStage(Stages.AreWeDoneYet)
+ transitionNext
+ {
+ }
+ function i()
+ timedTransitions
+ atStage(Stages.Finished)
+ {
+ }
+ }
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..48664344
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Solidity documentation build configuration file, created by
+# sphinx-quickstart on Mon Dec 7 12:32:57 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+
+def setup(sphinx):
+ sys.path.insert(0, os.path.abspath('./utils'))
+ from SolidityLexer import SolidityLexer
+ sphinx.add_lexer('Solidity', SolidityLexer())
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'Solidity'
+copyright = '2015, Ethereum'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.2.0'
+# The full version, including alpha/beta/rc tags.
+release = '0.2.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+highlight_language = 'Solidity'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Soliditydoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'solidity.tex', 'Solidity Documentation', 'Ethereum', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs/contracts.rst b/docs/contracts.rst
new file mode 100644
index 00000000..2ab03849
--- /dev/null
+++ b/docs/contracts.rst
@@ -0,0 +1,884 @@
+.. index:: ! contract
+
+##########
+Contracts
+##########
+
+Contracts in Solidity are what classes are in object oriented languages. They
+contain persistent data in state variables and functions that can modify these
+variables. Calling a function on a different contract (instance) will perform
+an EVM function call and thus switch the context such that state variables are
+inaccessible.
+
+.. index:: ! contract;creation
+
+******************
+Creating Contracts
+******************
+
+Contracts can be created "from outside" or from Solidity contracts.
+When a contract is created, its constructor (a function with the same
+name as the contract) is executed once.
+
+From `web3.js`, i.e. the JavaScript
+API, this is done as follows::
+
+ // The json abi array generated by the compiler
+ var abiArray = [
+ {
+ "inputs":[
+ {"name":"x","type":"uint256"},
+ {"name":"y","type":"uint256"}
+ ],
+ "type":"constructor"
+ },
+ {
+ "constant":true,
+ "inputs":[],
+ "name":"x",
+ "outputs":[{"name":"","type":"bytes32"}],
+ "type":"function"
+ }
+ ];
+
+ var MyContract = web3.eth.contract(abiArray);
+ // deploy new contract
+ var contractInstance = MyContract.new(
+ 10,
+ {from: myAccount, gas: 1000000}
+ );
+
+.. index:: constructor;arguments
+
+Internally, constructor arguments are passed after the code of
+the contract itself, but you do not have to care about this
+if you use `web3.js`.
+
+If a contract wants to create another contract, the source code
+(and the binary) of the created contract has to be known to the creator.
+This means that cyclic creation dependencies are impossible.
+
+::
+
+ contract OwnedToken {
+ // TokenCreator is a contract type that is defined below.
+ // It is fine to reference it as long as it is not used
+ // to create a new contract.
+ TokenCreator creator;
+ address owner;
+ bytes32 name;
+ // This is the constructor which registers the
+ // creator and the assigned name.
+ function OwnedToken(bytes32 _name) {
+ owner = msg.sender;
+ // We do an explicit type conversion from `address`
+ // to `TokenCreator` and assume that the type of
+ // the calling contract is TokenCreator, there is
+ // no real way to check that.
+ creator = TokenCreator(msg.sender);
+ name = _name;
+ }
+ function changeName(bytes32 newName) {
+ // Only the creator can alter the name --
+ // the comparison is possible since contracts
+ // are implicitly convertible to addresses.
+ if (msg.sender == creator) name = newName;
+ }
+ function transfer(address newOwner) {
+ // Only the current owner can transfer the token.
+ if (msg.sender != owner) return;
+ // We also want to ask the creator if the transfer
+ // is fine. Note that this calls a function of the
+ // contract defined below. If the call fails (e.g.
+ // due to out-of-gas), the execution here stops
+ // immediately.
+ if (creator.isTokenTransferOK(owner, newOwner))
+ owner = newOwner;
+ }
+ }
+
+ contract TokenCreator {
+ function createToken(bytes32 name)
+ returns (OwnedToken tokenAddress)
+ {
+ // Create a new Token contract and return its address.
+ // From the JavaScript side, the return type is simply
+ // "address", as this is the closest type available in
+ // the ABI.
+ return new OwnedToken(name);
+ }
+ function changeName(OwnedToken tokenAddress, bytes32 name) {
+ // Again, the external type of "tokenAddress" is
+ // simply "address".
+ tokenAddress.changeName(name);
+ }
+ function isTokenTransferOK(
+ address currentOwner,
+ address newOwner
+ ) returns (bool ok) {
+ // Check some arbitrary condition.
+ address tokenAddress = msg.sender;
+ return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
+ }
+ }
+
+.. index:: ! visibility, external, public, private, internal
+
+.. _visibility-and-accessors:
+
+************************
+Visibility and Accessors
+************************
+
+Since Solidity knows two kinds of function calls (internal
+ones that do not create an actual EVM call (also called
+a "message call") and external
+ones that do), there are four types of visibilities for
+functions and state variables.
+
+Functions can be specified as being `external`,
+`public`, `internal` or `private`, where the default is
+`public`. For state variables, `external` is not possible
+and the default is `internal`.
+
+`external`:
+ External functions are part of the contract
+ interface, which means they can be called from other contracts and
+ via transactions. An external function `f` cannot be called
+ internally (i.e. `f()` does not work, but `this.f()` works).
+ External functions are sometimes more efficient when
+ they receive large arrays of data.
+
+`public`:
+ Public functions are part of the contract
+ interface and can be either called internally or via
+ messages. For public state variables, an automatic accessor
+ function (see below) is generated.
+
+`internal`:
+ Those functions and state variables can only be
+ accessed internally (i.e. from within the current contract
+ or contracts deriving from it), without using `this`.
+
+`private`:
+ Private functions and state variables are only
+ visible for the contract they are defined in and not in
+ derived contracts.
+
+.. note::
+ Everything that is inside a contract is visible to
+ all external observers. Making something `private`
+ only prevents other contract from accessing and modifying
+ the information, but it will still be visible to the
+ whole world outside of the blockchain.
+
+The visibility specifier is given after the type for
+state variables and between parameter list and
+return parameter list for functions.
+
+::
+
+ contract c {
+ function f(uint a) private returns (uint b) { return a + 1; }
+ function setData(uint a) internal { data = a; }
+ uint public data;
+ }
+
+Other contracts can call `c.data()` to retrieve the value of data in state
+storage, but are not able to call `f`. Contracts derived from `c` can call
+`setData` to alter the value of `data` (but only in their own state).
+
+.. index:: ! accessor;function, ! function;accessor
+
+Accessor Functions
+==================
+
+The compiler automatically creates accessor functions for
+all public state variables. The contract given below will
+have a function called `data` that does not take any
+arguments and returns a uint, the value of the state
+variable `data`. The initialization of state variables can
+be done at declaration.
+
+The accessor functions have external visibility. If the
+symbol is accessed internally (i.e. without `this.`),
+it is a state variable and if it is accessed externally
+(i.e. with `this.`), it is a function.
+
+::
+
+ contract test {
+ uint public data = 42;
+ }
+
+The next example is a bit more complex:
+
+::
+
+ contract complex {
+ struct Data { uint a; bytes3 b; mapping(uint => uint) map; }
+ mapping(uint => mapping(bool => Data[])) public data;
+ }
+
+It will generate a function of the following form::
+
+ function data(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b)
+ {
+ a = data[arg1][arg2][arg3].a;
+ b = data[arg1][arg2][arg3].b;
+ }
+
+Note that the mapping in the struct is omitted because there
+is no good way to provide the key for the mapping.
+
+.. index:: ! function;modifier
+
+.. _modifiers:
+
+******************
+Function Modifiers
+******************
+
+Modifiers can be used to easily change the behaviour of functions, for example
+to automatically check a condition prior to executing the function. They are
+inheritable properties of contracts and may be overridden by derived contracts.
+
+::
+
+ contract owned {
+ function owned() { owner = msg.sender; }
+ address owner;
+
+ // This contract only defines a modifier but does not use
+ // it - it will be used in derived contracts.
+ // The function body is inserted where the special symbol
+ // "_" in the definition of a modifier appears.
+ // This means that if the owner calls this function, the
+ // function is executed and otherwise, an exception is
+ // thrown.
+ modifier onlyowner { if (msg.sender != owner) throw; _ }
+ }
+ contract mortal is owned {
+ // This contract inherits the "onlyowner"-modifier from
+ // "owned" and applies it to the "close"-function, which
+ // causes that calls to "close" only have an effect if
+ // they are made by the stored owner.
+ function close() onlyowner {
+ selfdestruct(owner);
+ }
+ }
+ contract priced {
+ // Modifiers can receive arguments:
+ modifier costs(uint price) { if (msg.value >= price) _ }
+ }
+ contract Register is priced, owned {
+ mapping (address => bool) registeredAddresses;
+ uint price;
+ function Register(uint initialPrice) { price = initialPrice; }
+ function register() costs(price) {
+ registeredAddresses[msg.sender] = true;
+ }
+ function changePrice(uint _price) onlyowner {
+ price = _price;
+ }
+ }
+
+Multiple modifiers can be applied to a function by specifying them in a
+whitespace-separated list and will be evaluated in order. Explicit returns from
+a modifier or function body immediately leave the whole function, while control
+flow reaching the end of a function or modifier body continues after the "_" in
+the preceding modifier. Arbitrary expressions are allowed for modifier
+arguments and in this context, all symbols visible from the function are
+visible in the modifier. Symbols introduced in the modifier are not visible in
+the function (as they might change by overriding).
+
+.. index:: ! constant
+
+**********
+Constants
+**********
+
+State variables can be declared as constant (this is not yet implemented
+for array and struct types and not possible for mapping types).
+
+::
+
+ contract C {
+ uint constant x = 32**22 + 8;
+ string constant text = "abc";
+ }
+
+This has the effect that the compiler does not reserve a storage slot
+for these variables and every occurrence is replaced by their constant value.
+
+The value expression can only contain integer arithmetics.
+
+
+.. index:: ! fallback function, function;fallback
+
+.. _fallback-function:
+
+*****************
+Fallback Function
+*****************
+
+A contract can have exactly one unnamed function. This function cannot have
+arguments and is executed on a call to the contract if none of the other
+functions matches the given function identifier (or if no data was supplied at
+all).
+
+Furthermore, this function is executed whenever the contract receives plain
+Ether (witout data). In such a context, there is very little gas available to
+the function call, so it is important to make fallback functions as cheap as
+possible.
+
+::
+
+ contract Test {
+ function() { x = 1; }
+ uint x;
+ }
+
+ // This contract rejects any Ether sent to it. It is good
+ // practise to include such a function for every contract
+ // in order not to loose Ether.
+ contract Rejector {
+ function() { throw; }
+ }
+
+ contract Caller {
+ function callTest(address testAddress) {
+ Test(testAddress).call(0xabcdef01); // hash does not exist
+ // results in Test(testAddress).x becoming == 1.
+ Rejector r = Rejector(0x123);
+ r.send(2 ether);
+ // results in r.balance == 0
+ }
+ }
+
+.. index:: ! event
+
+.. _events:
+
+******
+Events
+******
+
+Events allow the convenient usage of the EVM logging facilities,
+which in turn can be used to "call" JavaScript callbacks in the user interface
+of a dapp, which listen for these events.
+
+Events are
+inheritable members of contracts. When they are called, they cause the
+arguments to be stored in the transaction's log - a special data structure
+in the blockchain. These logs are associated with the address of
+the contract and will be incorporated into the blockchain
+and stay there as long as a block is accessible (forever as of
+Frontier and Homestead, but this might change with Serenity). Log and
+event data is not accessible from within contracts (not even from
+the contract that created a log).
+
+SPV proofs for logs are possible, so if an external entity supplies
+a contract with such a proof, it can check that the log actually
+exists inside the blockchain (but be aware of the fact that
+ultimately, also the block headers have to be supplied because
+the contract can only see the last 256 block hashes).
+
+Up to three parameters can
+receive the attribute `indexed` which will cause the respective arguments
+to be searched for: It is possible to filter for specific values of
+indexed arguments in the user interface.
+
+If arrays (including `string` and `bytes`) are used as indexed arguments, the
+sha3-hash of it is stored as topic instead.
+
+The hash of the signature of the event is one of the topics except if you
+declared the event with `anonymous` specifier. This means that it is
+not possible to filter for specific anonymous events by name.
+
+All non-indexed arguments will be stored in the data part of the log.
+
+::
+
+ contract ClientReceipt {
+ event Deposit(
+ address indexed _from,
+ bytes32 indexed _id,
+ uint _value
+ );
+ function deposit(bytes32 _id) {
+ // Any call to this function (even deeply nested) can
+ // be detected from the JavaScript API by filtering
+ // for `Deposit` to be called.
+ Deposit(msg.sender, _id, msg.value);
+ }
+ }
+
+The use in the JavaScript API would be as follows:
+
+::
+
+ var abi = /* abi as generated by the compiler */;
+ var ClientReceipt = web3.eth.contract(abi);
+ var clientReceipt = ClientReceipt.at(0x123 /* address */);
+
+ var event = clientReceipt.Deposit();
+
+ // watch for changes
+ event.watch(function(error, result){
+ // result will contain various information
+ // including the argumets given to the Deposit
+ // call.
+ if (!error)
+ console.log(result);
+ });
+
+ // Or pass a callback to start watching immediately
+ var event = clientReceipt.Deposit(function(error, result) {
+ if (!error)
+ console.log(result);
+ });
+
+.. index:: ! log
+
+Low-Level Interface to Logs
+===========================
+
+It is also possible to access the low-level interface to the logging
+mechanism via the functions `log0`, `log1`, `log2`, `log3` and `log4`.
+`logi` takes `i + 1` parameter of type `bytes32`, where the first
+argument will be used for the data part of the log and the others
+as topics. The event call above can be performed in the same way as
+
+::
+
+ log3(
+ msg.value,
+ 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,
+ msg.sender,
+ _id
+ );
+
+where the long hexadecimal number is equal to
+`sha3("Deposit(address,hash256,uint256)")`, the signature of the event.
+
+Additional Resources for Understanding Events
+==============================================
+
+- `Javascript documentation <https://github.com/ethereum/wiki/wiki/JavaScript-API#contract-events>`_
+- `Example usage of events <https://github.com/debris/smart-exchange/blob/master/lib/contracts/SmartExchange.sol>`_
+- `How to access them in js <https://github.com/debris/smart-exchange/blob/master/lib/exchange_transactions.js>`_
+
+.. index:: ! inheritance, ! base class, ! contract;base, ! deriving
+
+***********
+Inheritance
+***********
+
+Solidity supports multiple inheritance by copying code including polymorphism.
+
+All function calls are virtual, which means that the most derived function
+is called, except when the contract is explicitly given.
+
+Even if a contract inherits from multiple other contracts, only a single
+contract is created on the blockchain, the code from the base contracts
+is always copied into the final contract.
+
+The general inheritance system is very similar to
+`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,
+especially concerning multiple inheritance.
+
+Details are given in the following example.
+
+::
+
+ contract owned {
+ function owned() { owner = msg.sender; }
+ address owner;
+ }
+
+ // Use "is" to derive from another contract. Derived
+ // contracts can access all non-private members including
+ // internal functions and state variables. These cannot be
+ // accessed externally via `this`, though.
+ contract mortal is owned {
+ function kill() {
+ if (msg.sender == owner) selfdestruct(owner);
+ }
+ }
+
+ // These abstract contracts are only provided to make the
+ // interface known to the compiler. Note the function
+ // without body. If a contract does not implement all
+ // functions it can only be used as an interface.
+ contract Config {
+ function lookup(uint id) returns (address adr);
+ }
+ contract NameReg {
+ function register(bytes32 name);
+ function unregister();
+ }
+
+ // Multiple inheritance is possible. Note that "owned" is
+ // also a base class of "mortal", yet there is only a single
+ // instance of "owned" (as for virtual inheritance in C++).
+ contract named is owned, mortal {
+ function named(bytes32 name) {
+ Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
+ NameReg(config.lookup(1)).register(name);
+ }
+
+ // Functions can be overridden, both local and
+ // message-based function calls take these overrides
+ // into account.
+ function kill() {
+ if (msg.sender == owner) {
+ Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
+ NameReg(config.lookup(1)).unregister();
+ // It is still possible to call a specific
+ // overridden function.
+ mortal.kill();
+ }
+ }
+ }
+
+ // If a constructor takes an argument, it needs to be
+ // provided in the header (or modifier-invocation-style at
+ // the constructor of the derived contract (see below)).
+ contract PriceFeed is owned, mortal, named("GoldFeed") {
+ function updateInfo(uint newInfo) {
+ if (msg.sender == owner) info = newInfo;
+ }
+
+ function get() constant returns(uint r) { return info; }
+
+ uint info;
+ }
+
+Note that above, we call `mortal.kill()` to "forward" the
+destruction request. The way this is done is problematic, as
+seen in the following example::
+
+ contract mortal is owned {
+ function kill() {
+ if (msg.sender == owner) selfdestruct(owner);
+ }
+ }
+ contract Base1 is mortal {
+ function kill() { /* do cleanup 1 */ mortal.kill(); }
+ }
+ contract Base2 is mortal {
+ function kill() { /* do cleanup 2 */ mortal.kill(); }
+ }
+ contract Final is Base1, Base2 {
+ }
+
+A call to `Final.kill()` will call `Base2.kill` as the most
+derived override, but this function will bypass
+`Base1.kill`, basically because it does not even know about
+`Base1`. The way around this is to use `super`::
+
+ contract mortal is owned {
+ function kill() {
+ if (msg.sender == owner) selfdestruct(owner);
+ }
+ }
+ contract Base1 is mortal {
+ function kill() { /* do cleanup 1 */ super.kill(); }
+ }
+ contract Base2 is mortal {
+ function kill() { /* do cleanup 2 */ super.kill(); }
+ }
+ contract Final is Base2, Base1 {
+ }
+
+If `Base1` calls a function of `super`, it does not simply
+call this function on one of its base contracts, it rather
+calls this function on the next base contract in the final
+inheritance graph, so it will call `Base2.kill()` (note that
+the final inheritance sequence is -- starting with the most
+derived contract: Final, Base1, Base2, mortal, owned).
+The actual function that is called when using super is
+not known in the context of the class where it is used,
+although its type is known. This is similar for ordinary
+virtual method lookup.
+
+.. index:: ! base;constructor
+
+Arguments for Base Constructors
+===============================
+
+Derived contracts need to provide all arguments needed for
+the base constructors. This can be done at two places::
+
+ contract Base {
+ uint x;
+ function Base(uint _x) { x = _x; }
+ }
+ contract Derived is Base(7) {
+ function Derived(uint _y) Base(_y * _y) {
+ }
+ }
+
+Either directly in the inheritance list (`is Base(7)`) or in
+the way a modifier would be invoked as part of the header of
+the derived constructor (`Base(_y * _y)`). The first way to
+do it is more convenient if the constructor argument is a
+constant and defines the behaviour of the contract or
+describes it. The second way has to be used if the
+constructor arguments of the base depend on those of the
+derived contract. If, as in this silly example, both places
+are used, the modifier-style argument takes precedence.
+
+.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
+
+Multiple Inheritance and Linearization
+======================================
+
+Languages that allow multiple inheritance have to deal with
+several problems, one of them being the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_.
+Solidity follows the path of Python and uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_"
+to force a specific order in the DAG of base classes. This
+results in the desirable property of monotonicity but
+disallows some inheritance graphs. Especially, the order in
+which the base classes are given in the `is` directive is
+important. In the following code, Solidity will give the
+error "Linearization of inheritance graph impossible".
+
+::
+
+ contract X {}
+ contract A is X {}
+ contract C is A, X {}
+
+The reason for this is that `C` requests `X` to override `A`
+(by specifying `A, X` in this order), but `A` itself
+requests to override `X`, which is a contradiction that
+cannot be resolved.
+
+A simple rule to remember is to specify the base classes in
+the order from "most base-like" to "most derived".
+
+.. index:: ! contract;abstract, ! abstract contract
+
+******************
+Abstract Contracts
+******************
+
+Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by `;`)::
+
+ contract feline {
+ function utterance() returns (bytes32);
+ }
+
+Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
+
+ contract Cat is feline {
+ function utterance() returns (bytes32) { return "miaow"; }
+ }
+
+If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract.
+
+.. index:: ! library, callcode, delegatecall
+
+.. _libraries:
+
+************
+Libraries
+************
+
+Libraries are similar to contracts, but their purpose is that they are deployed
+only once at a specific address and their code is reused using the `DELEGATECALL`
+(`CALLCODE` until homestead)
+feature of the EVM. This means that if library functions are called, their code
+is executed in the context of the calling contract, i.e. `this` points to the
+calling contract and especially the storage from the calling contract can be
+accessed. As a library is an isolated piece of source code, it can only access
+state variables of the calling contract if they are explicitly supplied (it
+would have to way to name them, otherwise).
+
+.. index:: using for, set
+
+The following example illustrates how to use libraries (but
+be sure to check out :ref:`using for <using-for>` for a
+more advanced example to implement a set).
+
+::
+
+ library Set {
+ // We define a new struct datatype that will be used to
+ // hold its data in the calling contract.
+ struct Data { mapping(uint => bool) flags; }
+ // Note that the first parameter is of type "storage
+ // reference" and thus only its storage address and not
+ // its contents is passed as part of the call. This is a
+ // special feature of library functions. It is idiomatic
+ // to call the first parameter 'self', if the function can
+ // be seen as a method of that object.
+ function insert(Data storage self, uint value)
+ returns (bool)
+ {
+ if (self.flags[value])
+ return false; // already there
+ self.flags[value] = true;
+ return true;
+ }
+ function remove(Data storage self, uint value)
+ returns (bool)
+ {
+ if (!self.flags[value])
+ return false; // not there
+ self.flags[value] = false;
+ return true;
+ }
+ function contains(Data storage self, uint value)
+ returns (bool)
+ {
+ return self.flags[value];
+ }
+ }
+ contract C {
+ Set.Data knownValues;
+ function register(uint value) {
+ // The library functions can be called without a
+ // specific instance of the library, since the
+ // "instance" will be the current contract.
+ if (!Set.insert(knownValues, value))
+ throw;
+ }
+ // In this contract, we can also directly access knownValues.flags, if we want.
+ }
+
+Of course, you do not have to follow this way to use
+libraries - they can also be used without defining struct
+data types, functions also work without any storage
+reference parameters, can have multiple storage reference
+parameters and in any position.
+
+The calls to `Set.contains`, `Set.insert` and `Set.remove`
+are all compiled as calls (`DELEGATECALL`s) to an external
+contract/library. If you use libraries, take care that an
+actual external function call is performed.
+`msg.sender`, `msg.value` and `this` will retain their values
+in this call, though (prior to Homestead, `msg.sender` and
+`msg.value` changed, though).
+
+As the compiler cannot know where the library will be
+deployed at, these addresses have to be filled into the
+final bytecode by a linker (see [Using the Commandline
+Compiler](#using-the-commandline-compiler) on how to use the
+commandline compiler for linking). If the addresses are not
+given as arguments to the compiler, the compiled hex code
+will contain placeholders of the form `__Set______` (where
+`Set` is the name of the library). The address can be filled
+manually by replacing all those 40 symbols by the hex
+encoding of the address of the library contract.
+
+Restrictions for libraries in comparison to contracts:
+
+- no state variables
+- cannot inherit nor be inherited
+
+(these might be lifted at a later point)
+
+.. index:: ! using for, library
+
+.. _using-for:
+
+*********
+Using For
+*********
+
+The directive `using A for B;` can be used to attach library
+functions (from the library `A`) to any type (`B`).
+These functions will receive the object they are called on
+as their first parameter (like the `self` variable in
+Python).
+
+The effect of `using A for *;` is that the functions from
+the library `A` are attached to any type.
+
+In both situations, all functions, even those where the
+type of the first parameter does not match the type of
+the object, are attached. The type is checked at the
+point the function is called and function overload
+resolution is performed.
+
+The `using A for B;` directive is active for the current
+scope, which is limited to a contract for now but will
+be lifted to the global scope later, so that by including
+a module, its data types including library functions are
+available without having to add further code.
+
+Let us rewrite the set example from the
+:ref:`libraries` in this way::
+
+ // This is the same code as before, just without comments
+ library Set {
+ struct Data { mapping(uint => bool) flags; }
+ function insert(Data storage self, uint value)
+ returns (bool)
+ {
+ if (self.flags[value])
+ return false; // already there
+ self.flags[value] = true;
+ return true;
+ }
+ function remove(Data storage self, uint value)
+ returns (bool)
+ {
+ if (!self.flags[value])
+ return false; // not there
+ self.flags[value] = false;
+ return true;
+ }
+ function contains(Data storage self, uint value)
+ returns (bool)
+ {
+ return self.flags[value];
+ }
+ }
+
+ contract C {
+ using Set for Set.Data; // this is the crucial change
+ Set.Data knownValues;
+ function register(uint value) {
+ // Here, all variables of type Set.Data have
+ // corresponding member functions.
+ // The following function call is identical to
+ // Set.insert(knownValues, value)
+ if (!knownValues.insert(value))
+ throw;
+ }
+ }
+
+It is also possible to extend elementary types in that way::
+
+ library Search {
+ function indexOf(uint[] storage self, uint value) {
+ for (uint i = 0; i < self.length; i++)
+ if (self[i] == value) return i;
+ return uint(-1);
+ }
+ }
+
+ contract C {
+ using Search for uint[];
+ uint[] data;
+ function append(uint value) {
+ data.push(value);
+ }
+ function replace(uint _old, uint _new) {
+ // This performs the library function call
+ uint index = data.find(_old);
+ if (index == -1)
+ data.push(_new);
+ else
+ data[index] = _new;
+ }
+ }
+
+Note that all library calls are actual EVM function calls. This means that
+if you pass memory or value types, a copy will be performed, even of the
+`self` variable. The only situation where no copy will be performed
+is when storage reference variables are used.
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
new file mode 100644
index 00000000..9f0f5b5d
--- /dev/null
+++ b/docs/control-structures.rst
@@ -0,0 +1,169 @@
+##################################
+Expressions and Control Structures
+##################################
+
+.. index:: if, else, while, for, break, continue, return, switch, goto
+
+Control Structures
+===================
+
+Most of the control structures from C/JavaScript are available in Solidity
+except for `switch` and `goto`. So
+there is: `if`, `else`, `while`, `for`, `break`, `continue`, `return`, `? :`, with
+the usual semantics known from C / JavaScript.
+
+Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
+around single-statement bodies.
+
+Note that there is no type conversion from non-boolean to boolean types as
+there is in C and JavaScript, so `if (1) { ... }` is *not* valid Solidity.
+
+.. index:: ! function;call, function;internal, function;external
+
+.. _function-calls:
+
+Function Calls
+==============
+
+Internal Function Calls
+-----------------------
+
+Functions of the current contract can be called directly ("internally"), also recursively, as seen in
+this nonsensical example::
+
+ contract c {
+ function g(uint a) returns (uint ret) { return f(); }
+ function f() returns (uint ret) { return g(7) + f(); }
+ }
+
+These function calls are translated into simple jumps inside the EVM. This has
+the effect that the current memory is not cleared, i.e. passing memory references
+to internally-called functions is very efficient. Only functions of the same
+contract can be called internally.
+
+External Function Calls
+-----------------------
+
+The expression `this.g(8);` is also a valid function call, but this time, the function
+will be called "externally", via a message call and not directly via jumps.
+Functions of other contracts have to be called externally. For an external call,
+all function arguments have to be copied to memory.
+
+When calling functions
+of other contracts, the amount of Wei sent with the call and the gas can be specified::
+
+ contract InfoFeed {
+ function info() returns (uint ret) { return 42; }
+ }
+ contract Consumer {
+ InfoFeed feed;
+ function setFeed(address addr) { feed = InfoFeed(addr); }
+ function callFeed() { feed.info.value(10).gas(800)(); }
+ }
+
+Note that the expression `InfoFeed(addr)` performs an explicit type conversion stating
+that "we know that the type of the contract at the given address is `InfoFeed`" and
+this does not execute a constructor. We could also have used `function setFeed(InfoFeed _feed) { feed = _feed; }` directly. Be careful about the fact that `feed.info.value(10).gas(800)`
+only (locally) sets the value and amount of gas sent with the function call and only the
+parentheses at the end perform the actual call.
+
+Named Calls and Anonymous Function Parameters
+---------------------------------------------
+
+Function call arguments can also be given by name, in any order, and the names
+of unused parameters (especially return parameters) can be omitted.
+
+::
+
+ contract c {
+ function f(uint key, uint value) { ... }
+ function g() {
+ // named arguments
+ f({value: 2, key: 3});
+ }
+ // omitted parameters
+ function func(uint k, uint) returns(uint) {
+ return k;
+ }
+ }
+
+Order of Evaluation of Expressions
+==================================
+
+The evaluation order of expressions is not specified (more formally, the order
+in which the children of one node in the expression tree are evaluated is not
+specified, but they are of course evaluated before the node itself). It is only
+guaranteed that statements are executed in order and short-circuiting for
+boolean expressions is done.
+
+.. index:: ! assignment
+
+Assignment
+==========
+
+.. index:: ! assignment;destructuring
+
+Destructuring Assignments and Returning Multiple Values
+-------------------------------------------------------
+
+Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time::
+
+ contract C {
+ uint[] data;
+ function f() returns (uint, bool, uint) {
+ return (7, true, 2);
+ }
+ function g() {
+ // Declares and assigns the variables. Specifying the type explicitly is not possible.
+ var (x, b, y) = f();
+ // Assigns to a pre-existing variable.
+ (x, y) = (2, 7);
+ // Common trick to swap values -- does not work for non-value storage types.
+ (x, y) = (y, x);
+ // Components can be left out (also for variable declarations).
+ // If the tuple ends in an empty component,
+ // the rest of the values are discarded.
+ (data.length,) = f(); // Sets the length to 7
+ // The same can be done on the left side.
+ (,data[3]) = f(); // Sets data[3] to 2
+ // Components can only be left out at the left-hand-side of assignments, with
+ // one exception:
+ (x,) = (1,);
+ // (1,) is the only way to specify a 1-component tuple, because (1) is
+ // equivalent to 1.
+ }
+ }
+
+Complications for Arrays and Structs
+------------------------------------
+
+The semantics of assignment are a bit more complicated for non-value types like arrays and structs.
+Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including `bytes` and `string`) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state.
+
+.. index:: ! exception, ! throw
+
+Exceptions
+==========
+
+There are some cases where exceptions are thrown automatically (see below). You can use the `throw` instruction to throw an exception manually. The effect of an exception is that the currently executing call is stopped and reverted (i.e. all changes to the state and balances are undone) and the exception is also "bubbled up" through Solidity function calls (exceptions are `send` and the low-level functions `call`, `delegatecall` and `callcode`, those return `false` in case of an exception).
+
+Catching exceptions is not yet possible.
+
+In the following example, we show how `throw` can be used to easily revert an Ether transfer and also how to check the return value of `send`::
+
+ contract Sharer {
+ function sendHalf(address addr) returns (uint balance) {
+ if (!addr.send(msg.value/2))
+ throw; // also reverts the transfer to Sharer
+ return this.balance;
+ }
+ }
+
+Currently, there are three situations, where exceptions happen automatically in Solidity:
+
+1. If you access an array beyond its length (i.e. `x[i]` where `i >= x.length`)
+2. If a function called via a message call does not finish properly (i.e. it runs out of gas or throws an exception itself).
+3. If a non-existent function on a library is called or Ether is sent to a library.
+
+Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect.
+
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
new file mode 100644
index 00000000..db37a272
--- /dev/null
+++ b/docs/frequently-asked-questions.rst
@@ -0,0 +1,785 @@
+###########################
+Frequently Asked Questions
+###########################
+
+This list was originally compiled by [fivedogit](mailto:fivedogit@gmail.com).
+
+
+***************
+Basic Questions
+***************
+
+What is Solidity?
+=============================
+
+Solidity is the DEV-created (i.e. Ethereum Foundation-created),
+Javascript-inspired language that can be used to create smart contracts
+on the Ethereum blockchain. There are other
+languages you can use as well (LLL, Serpent, etc). The main points in
+favour of Solidity is that it is statically typed and offers many
+advanced features like inheritance, libraries, complex
+user-defined types and a bytecode optimizer.
+
+Solidity contracts can be compiled a few different ways (see below) and the
+resulting output can be cut/pasted into a geth console to deploy them to the
+Ethereum blockchain.
+
+There are some `contract examples <https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts/>`_ by fivedogit and
+there should be a `test contract <https://github.com/ethereum/solidity/blob/develop/test/libsolidity/SolidityEndToEndTest.cpp>`_ for every single feature of Solidity.
+
+How do I compile contracts?
+=============================
+
+Probably the fastest way is the `online compiler <https://chriseth.github.io/browser-solidity/>`_.
+
+You can also use the `solc` binary which comes with cpp-ethereum to compile
+contracts or an emerging option is to use Mix, the IDE.
+
+
+Create and publish the most basic contract possible
+===================================================
+
+A quite simple contract is the `greeter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_
+
+Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction)
+=============================================================================================================
+
+Transactions are not guaranteed to happen on the next block or any future
+specific block, since it is up to the miners to include transactions and not up
+to the submitter of the transaction. This applies to function calls/transactions and contract
+creation transactions.
+
+If you want to schedule future calls of your contract, you can use the
+`alarm clock <http://www.ethereum-alarm-clock.com/>`_.
+
+What is the transaction "payload"?
+==================================
+
+This is just the bytecode "data" sent along with the request.
+
+Is there a decompiler available?
+================================
+
+There is no decompiler to Solidity. This is in principle possible
+to some degree, but for example variable names will be lost and
+great effort will be necessary to make it look similar to
+the original source code.
+
+Bytecode can be decompiled to opcodes, a service that is provided by
+several blockchain explorers.
+
+Contracts on the blockchain should have their original source
+code published if they are to be used by third parties.
+
+Does selfdestruct() free up space in the blockchain?
+====================================================
+
+It removes the contract bytecode and storage from the current block
+into the future, but since the blockchain stores every single block (i.e.
+all history), this will not actually free up space on full/achive nodes.
+
+Create a contract that can be killed and return funds
+=====================================================
+
+First, a word of warning: Killing contracts sounds like a good idea, because "cleaning up"
+is always good, but as seen above, it does not really clean up. Furthermore,
+if Ether is sent to removed contracts, the Ether will be forever lost.
+
+If you want to deactivate your contracts, rather **disable** them by changing some
+internal state which causes all functions to throw. This will make it impossible
+to use the contract and ether sent to the contract will be returned automatically.
+
+Now to answering the question: Inside a constructor, `msg.sender` is the
+creator. Save it. Then `selfdestruct(creator);` to kill and return funds.
+
+`example <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_
+
+Note that if you `import "mortal"` at the top of your contracts and declare
+`contract SomeContract is mortal { ...` and compile with a compiler that already
+has it (which includes `browser-solidity <https://chriseth.github.io/browser-solidity/>`_), then
+`kill()` is taken care of for you. Once a contract is "mortal", then you can
+`contractname.kill.sendTransaction({from:eth.coinbase})`, just the same as my
+examples.
+
+Store Ether in a contract
+=========================
+
+The trick is to create the contract with `{from:someaddress, value: web3.toWei(3,"ether")...}`
+
+See `endowment_retriever.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_.
+
+Use a non-constant function (req sendTransaction) to increment a variable in a contract
+=======================================================================================
+
+See `value_incrementer.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/20_value_incrementer.sol>`_.
+
+Get contract address in Solidity
+================================
+
+Short answer: The global variable `this` is the contract address.
+
+See `basic_info_getter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/15_basic_info_getter.sol>`_.
+
+Long answer: `this` is a variable representing the current contract.
+Its type is the type of the contract. Since any contract type basically inherits from the
+`address` type, `this` is always convertible to `address` and in this case contains
+its own address.
+
+What is the difference between a function marked constant and one that is not?
+==============================================================================
+
+`constant` functions can perform some action and return a value, but cannot
+change state (this is not yet enforced by the compiler). In other words, a
+constant function cannot save or update any variables within the contract or wider
+blockchain. These functions are called using `c.someFunction(...)` from
+geth or any other web3.js environment.
+
+"non-constant" functions (those lacking the `constant` specifier) must be called
+with `c.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000});`
+That is, because they can change state, they have to have a gas
+payment sent along to get the work done.
+
+Get a contract to return its funds to you (not using selfdestruct(...)).
+========================================================================
+
+This example demonstrates how to send funds from a contract to an address.
+
+See `endowment_retriever <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_.
+
+What is a mapping and how do we use them?
+=========================================
+
+A mapping is very similar to a K->V hashmap.
+If you have a state variable of type `mapping (string -> uint) x;`, then you can
+access the value by `x["somekeystring"]`.
+
+How can I get the length of a mapping?
+======================================
+
+Mappings are a rather low-level data structure. It does not store the keys
+and it is not possible to know which or how many values are "set". Actually,
+all values to all possible keys are set by default, they are just
+initialised with the zero value.
+
+In this sense, the attribute `length` for a mapping does not really apply.
+
+If you want to have a "sized mapping", you can use the iterable mapping
+(see below) or just a dynamically-sized array of structs.
+
+Are mappings iterable?
+======================
+
+Mappings themselves are not iterable, but you can use a higher-level
+datastructure on top of it, for example the `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_.
+
+Can you return an array or a string from a solidity function call?
+==================================================================
+
+Yes. See `array_receiver_and_returner.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/60_array_receiver_and_returner.sol>`_.
+
+What is problematic, though, is returning any variably-sized data (e.g. a
+variably-sized array like `uint[]`) from a fuction **called from within Solidity**.
+This is a limitation of the EVM and will be solved with the next protocol update.
+
+Returning variably-sized data as part of an external transaction or call is fine.
+
+How do you represent double/float in Solidity?
+==============================================
+
+This is not yet possible.
+
+Is it possible to in-line initialize an array like so: string[] myarray = ["a", "b"];
+=======================================================================================
+
+Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory
+array in the return statement. Pretty cool, huh?
+
+Example::
+
+ contract C {
+ function f() returns (uint8[5]) {
+ string[4] memory AdaArr = ["This", "is", "an", "array"];
+ return ([1, 2, 3, 4, 5]);
+ }
+ }
+
+What are events and why do we need them?
+========================================
+
+Let us suppose that you need a contract to alert the outside world when
+something happens. The contract can fire an event, which can be listened to
+from web3 (inside geth or a web application). The main advantage of events
+is that they are stored in a special way on the blockchain so that it
+is very easy to search for them.
+
+What are the different function visibilities?
+=============================================
+
+The visibility specifiers do not only change the visibility but also
+the way functions can be called. In general, functions in the
+same contract can also be called internally (which is cheaper
+and allows for memory types to be passed by reference). This
+is done if you just use `f(1,2)`. If you use `this.f(1,2)`
+or `otherContract.f(1,2)`, the function is called externally.
+
+Internal function calls have the advantage that you can use
+all Solidity types as parameters, but you have to stick to the
+simpler ABI types for external calls.
+
+* external: all, only externally
+
+* public: all (this is the default), externally and internally
+
+* internal: only this contract and contracts deriving from it, only internally
+
+* private: only this contract, only internally
+
+
+Do contract constructors have to be publicly visible?
+=====================================================
+
+You can use the visibility specifiers, but they do not yet have any effect.
+The constructor is removed from the contract code once it is deployed,
+
+Can a contract have multiple constructors?
+==========================================
+
+No, a contract can have only one constructor.
+
+More specifically, it can only have one function whose name matches
+that of the constructor.
+
+Having multiple constructors with different number of arguments
+or argument types, as it is possible in other languages
+is not allowed in Solidity.
+
+Is a constructor required?
+==========================
+
+No. If there is no constructor, a generic one without arguments and no actions will be used.
+
+Are timestamps (now, block.timestamp) reliable?
+===============================================
+
+This depends on what you mean by "reliable".
+In general, they are supplied by miners and are therefore vulnerable.
+
+Unless someone really messes up the blockchain or the clock on
+your computer, you can make the following assumptions:
+
+You publish a transaction at a time X, this transaction contains same
+code that calls `now` and is included in a block whose timestamp is Y
+and this block is included into the canonical chain (published) at a time Z.
+
+The value of `now` will be identical to Y and X <= Y <= Z.
+
+Never use `now` or `block.hash` as a source of randomness, unless you know
+what you are doing!
+
+Can a contract function return a struct?
+========================================
+
+Yes, but only in "internal" function calls.
+
+If I return an enum, I only get integer values in web3.js. How to get the named values?
+=======================================================================================
+
+Enums are not supported by the ABI, they are just supported by Solidity.
+You have to do the mapping yourself for now, we might provide some help
+later.
+
+What is the deal with "function () { ... }" inside Solidity contracts? How can a function not have a name?
+==========================================================================================================
+
+This function is called "fallback function" and it
+is called when someone just sent Ether to the contract without
+providing any data or if someone messed up the types so that they tried to
+call a function that does not exist.
+
+The default behaviour (if no fallback function is explicitly given) in
+these situations is to just accept the call and do nothing.
+This is desireable in many cases, but should only be used if there is
+a way to pull out Ether from a contract.
+
+If the contract is not meant to receive Ether with simple transfers, you
+should implement the fallback function as
+
+`function() { throw; }`
+
+this will cause all transactions to this contract that do not call an
+existing function to be reverted, so that all Ether is sent back.
+
+Another use of the fallback function is to e.g. register that your
+contract received ether by using an event.
+
+*Attention*: If you implement the fallback function take care that it uses as
+little gas as possible, because `send()` will only supply a limited amount.
+
+Is it possible to pass arguments to the fallback function?
+==========================================================
+
+The fallback function cannot take parameters.
+
+Under special circumstances, you can send data. If you take care
+that none of the other functions is invoked, you can access the data
+by `msg.data`.
+
+Can state variables be initialized in-line?
+===========================================
+
+Yes, this is possible for all types (even for structs). However, for arrays it
+should be noted that you must declare them as static memory arrays.
+
+Examples::
+
+ contract C {
+ struct S { uint a; uint b; }
+ S public x = S(1, 2);
+ string name = "Ada";
+ string[4] memory AdaArr = ["This", "is", "an", "array"];
+ }
+ contract D {
+ C c = new C();
+ }
+
+What is the "modifier" keyword?
+===============================
+
+Modifiers are a way to prepend or append code to a function in order
+to add guards, initialisation or cleanup functionality in a concise way.
+
+For examples, see the `features.sol <https://github.com/ethereum/dapp-bin/blob/master/library/features.sol>`_.
+
+How do structs work?
+====================
+
+See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_.
+
+How do for loops work?
+======================
+
+Very similar to JavaScript. There is one point to watch out for, though:
+
+If you use `for (var i = 0; i < a.length; i ++) { a[i] = i; }`, then
+the type of `i` will be inferred only from `0`, whose type is `uint8`.
+This means that if `a` has more than `255` elements, your loop will
+not terminate because `i` can only hold values up to `255`.
+
+Better use `for (uint i = 0; i < a.length...`
+
+See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_.
+
+What character set does Solidity use?
+=====================================
+
+Solidity is character set agnostic concerning strings in the source code, although
+utf-8 is recommended. Identifiers (variables, functions, ...) can only use
+ASCII.
+
+What are some examples of basic string manipulation (substring, indexOf, charAt, etc)?
+======================================================================================
+
+There are some string utility functions at `stringUtils.sol <https://github.com/ethereum/dapp-bin/blob/master/library/stringUtils.sol>`_
+which will be extended in the future.
+
+For now, if you want to modify a string (even when you only want to know its length),
+you should always convert it to a `bytes` first::
+
+ contract C {
+ string s;
+ function append(byte c) {
+ bytes(s).push(c);
+ }
+ function set(uint i, byte c) {
+ bytes(s)[i] = c;
+ }
+ }
+
+
+Can I concatenate two strings?
+==============================
+
+You have to do it manually for now.
+
+Why is the low-level function .call() less favorable than instantiating a contract with a variable (ContractB b;) and executing its functions (b.doSomething();)?
+=================================================================================================================================================================
+
+If you use actual functions, the compiler will tell you if the types
+or your arguments do not match, if the function does not exist
+or is not visible and it will do the packing of the
+arguments for you.
+
+See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and
+`pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
+
+Is unused gas automatically refunded?
+=====================================
+
+Yes and it is immediate, i.e. done as part of the transaction.
+
+When returning a value of say "uint" type, is it possible to return an "undefined" or "null"-like value?
+========================================================================================================
+
+This is not possible, because all types use up the full value range.
+
+You have the option to `throw` on error, which will also revert the whole
+transaction, which might be a good idea if you ran into an unexpected
+situation.
+
+If you do not want to throw, you can return a pair::
+
+ contract C {
+ uint[] counters;
+ function getCounter(uint index)
+ returns (uint counter, bool error) {
+ if (index >= counters.length) return (0, true);
+ else return (counters[index], false);
+ }
+ function checkCounter(uint index) {
+ var (counter, error) = getCounter(index);
+ if (error) { ... }
+ else { ... }
+ }
+ }
+
+
+Are comments included with deployed contracts and do they increase deployment gas?
+==================================================================================
+
+No, everything that is not needed for execution is removed during compilation.
+This includes, among others, comments, variable names and type names.
+
+What happens if you send ether along with a function call to a contract?
+========================================================================
+
+It gets added to the total balance of the contract, just like when you send ether when creating a contract.
+
+Is it possible to get a tx receipt for a transaction executed contract-to-contract?
+===================================================================================
+
+No, a function call from one contract to another does not create its own transaction,
+you have to look in the overall transaction. This is also the reason why several
+block explorer do not show Ether sent between contracts correctly.
+
+What is the memory keyword? What does it do?
+============================================
+
+The Ethereum Virtual Machine has three areas where it can store items.
+
+The first is "storage", where all the contract state variables reside.
+Every contract has its own storage and it is persistent between function calls
+and quite expensive to use.
+
+The second is "memory", this is used to hold temporary values. It
+is erased between (external) function calls and is cheaper to use.
+
+The third one is the stack, which is used to hold small local variables.
+It is almost free to use, but can only hold a limited amount of values.
+
+For almost all types, you cannot specify where they should be stored, because
+they are copied everytime they are used.
+
+The types where the so-called storage location is important are structs
+and arrays. If you e.g. pass such variables in function calls, their
+data is not copied if it can stay in memory or stay in storage.
+This means that you can modify their content in the called function
+and these modifications will still be visible in the caller.
+
+There are defaults for the storage location depending on which type
+of variable it concerns:
+
+* state variables are always in storage
+* function arguments are always in memory
+* local variables always reference storage
+
+Example::
+
+ contract C {
+ uint[] data1;
+ uint[] data2;
+ function appendOne() {
+ append(data1);
+ }
+ function appendTwo() {
+ append(data2);
+ }
+ function append(uint[] storage d) {
+ d.push(1);
+ }
+ }
+
+The function `append` can work both on `data1` and `data2` and its modifications will be
+stored permanently. If you remove the `storage` keyword, the default
+is to use `memory` for function arguments. This has the effect that
+at the point where `append(data1)` or `append(data2)` is called, an
+independent copy of the state variable is created in memory and
+`append` operates on this copy (which does not support `.push` - but that
+is another issue). The modifications to this independent copy do not
+carry back to `data1` or `data2`.
+
+A common mistake is to declare a local variable and assume that it will
+be created in memory, although it will be created in storage::
+
+ /// THIS CONTRACT CONTAINS AN ERROR
+ contract C {
+ uint someVariable;
+ uint[] data;
+ function f() {
+ uint[] x;
+ x.push(2);
+ data = x;
+ }
+ }
+
+The type of the local variable `x` is `uint[] storage`, but since
+storage is not dynamically allocated, it has to be assigned from
+a state variable before it can be used. So no space in storage will be
+allocated for `x`, but instead it functions only as an alias for
+a pre-existing variable in storage.
+
+What will happen is that the compiler interprets `x` as a storage
+pointer and will make it point to the storage slot `0` by default.
+This has the effect that `someVariable` (which resides at storage
+slot `0`) is modified by `x.push(2)`.
+
+The correct way to do this is the following::
+
+ contract C {
+ uint someVariable;
+ uint[] data;
+ function f() {
+ uint[] x = data;
+ x.push(2);
+ }
+ }
+
+Can a regular (i.e. non-contract) ethereum account be closed permanently like a contract can?
+=============================================================================================
+
+No. Non-contract accounts "exist" as long as the private key is known by
+someone or can be generated in some way.
+
+What is the difference between `bytes` and `byte[]`?
+====================================================
+
+`bytes` is usually more efficient: When used as arguments to functions (i.e. in
+CALLDATA) or in memory, every single element of a `byte[]` is padded to 32
+bytes which wastes 31 bytes per element.
+
+Is it possible to send a value while calling an overloaded function?
+====================================================================
+
+It's a known missing feature. https://www.pivotaltracker.com/story/show/92020468
+as part of https://www.pivotaltracker.com/n/projects/1189488
+
+Best solution currently see is to introduce a special case for gas and value and
+just re-check whether they are present at the point of overload resolution.
+
+
+******************
+Advanced Questions
+******************
+
+How do you get a random number in a contract? (Implement a self-returning gambling contract.)
+=============================================================================================
+
+Getting randomness right is often the crucial part in a crypto project and
+most failures result from bad random number generators.
+
+If you do not want it to be safe, you build something similar to the `coin flipper <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/35_coin_flipper.sol>`_
+but otherwise, rather use a contract that supplies randomness, like the `RANDAO <https://github.com/randao/randao>`_.
+
+Get return value from non-constant function from another contract
+=================================================================
+
+The key point is that the calling contract needs to know about the function it intends to call.
+
+See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_
+and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
+
+Get contract to do something when it is first mined
+===================================================
+
+Use the constructor. Anything inside it will be executed when the contract is first mined.
+
+See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_.
+
+Can a contract create another contract?
+=======================================
+
+Yes, see `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_.
+
+Note that the full code of the created contract has to be included in the creator contract.
+This also means that cyclic creations are not possible (because the contract would have
+to contain its own code) - at least not in a general way.
+
+How do you create 2-dimensional arrays?
+=======================================
+
+See `2D_array.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/55_2D_array.sol>`_.
+
+Note that filling a 10x10 square of `uint8` + contract creation took more than `800,000`
+gas at the time of this writing. 17x17 took `2,000,000` gas. With the limit at
+3.14 million... well, there’s a pretty low ceiling for what you can create right
+now.
+
+Note that merely "creating" the array is free, the costs are in filling it.
+
+Note2: Optimizing storage access can pull the gas costs down considerably, because
+32 `uint8` values can be stored in a single slot. The problem is that these optimizations
+currently do not work across loops and also have a problem with bounds checking.
+You might get much better results in the future, though.
+
+What does p.recipient.call.value(p.amount)(p.data) do?
+======================================================
+
+Every external function call in Solidity can be modified in two ways:
+
+1. You can add Ether together with the call
+2. You can limit the amount of gas available to the call
+
+This is done by "calling a function on the function":
+
+`f.gas(2).value(20)()` calls the modified function `f` and thereby sending 20
+Wei and limiting the gas to 2 (so this function call will most likely go out of
+gas and return your 20 Wei).
+
+In the above example, the low-level function `call` is used to invoke another
+contract with `p.data` as payload and `p.amount` Wei is sent with that call.
+
+Can a contract function accept a two-dimensional array?
+=======================================================
+
+This is not yet implemented for external calls and dynamic arrays -
+you can only use one level of dynamic arrays.
+
+What is the relationship between bytes32 and string? Why is it that ‘bytes32 somevar = "stringliteral";’ works and what does the saved 32-byte hex value mean?
+==============================================================================================================================================================
+
+The type `bytes32` can hold 32 (raw) bytes. In the assignment `bytes32 samevar = "stringliteral";`,
+the string literal is interpreted in its raw byte form and if you inspect `somevar` and
+see a 32-byte hex value, this is just `"stringliteral"` in hex.
+
+The type `bytes` is similar, only that it can change its length.
+
+Finally, `string` is basically identical to `bytes` only that it is assumed
+to hold the utf-8 encoding of a real string. Since `string` stores the
+data in utf-8 encoding it is quite expensive to compute the number of
+characters in the string (the encoding of some characters takes more
+than a single byte). Because of that, `string s; s.length` is not yet
+supported and not even index access `s[2]`. But if you want to access
+the low-level byte encoding of the string, you can use
+`bytes(s).length` and `bytes(s)[2]` which will result in the number
+of bytes in the utf-8 encoding of the string (not the number of
+characters) and the second byte (not character) of the utf-8 encoded
+string, respectively.
+
+
+Can a contract pass an array (static size) or string or bytes (dynamic size) to another contract?
+=================================================================================================
+
+Sure. Take care that if you cross the memory / storage boundary,
+independent copies will be created::
+
+ contract C {
+ uint[20] x;
+ function f() {
+ g(x);
+ h(x);
+ }
+ function g(uint[20] y) {
+ y[2] = 3;
+ }
+ function h(uint[20] storage y) {
+ y[3] = 4;
+ }
+
+The call to `g(x)` will not have an effect on `x` because it needs
+to create an independent copy of the storage value in memory
+(the default storage location is memory). On the other hand,
+`h(x)` successfully modifies `x` because only a reference
+and not a copy is passed.
+
+Sometimes, when I try to change the length of an array with ex: "arrayname.length = 7;" I get a compiler error "Value must be an lvalue". Why?
+==============================================================================================================================================
+
+You can resize a dynamic array in storage (i.e. an array declared at the
+contract level) with `arrayname.length = <some new length>;`. If you get the
+"lvalue" error, you are probably doing one of two things wrong.
+
+1. You might be trying to resize an array in "memory", or
+
+2. You might be trying to resize a non-dynamic array.
+
+::
+
+ int8[] memory memArr; // Case 1
+ memArr.length++; // illegal
+ int8[5] storageArr; // Case 2
+ somearray.length++; // legal
+ int8[5] storage storageArr2; // Explicit case 2
+ somearray2.length++; // legal
+
+**Important note:** In Solidity, array dimensions are declared backwards from the way you
+might be used to declaring them in C or Java, but they are access as in
+C or Java.
+
+For example, `int8[][5] somearray;` are 5 dynamic `int8` arrays.
+
+The reason for this is that `T[5]` is always an array of 5 `T`s,
+no matter whether `T` itself is an array or not (this is not the
+case in C or Java).
+
+Is it possible to return an array of strings ( string[] ) from a Solidity function?
+===================================================================================
+
+Not yet, as this requires two levels of dynamic arrays (`string` is a dynamic array itself).
+
+If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
+===========================================================================================================================
+
+The automatic accessor function for a public state variable of array type only returns
+individual elements. If you want to return the complete array, you have to
+manually write a function to do that.
+
+
+What could have happened if an account has storage value/s but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
+=========================================================================================================================================================
+
+The last thing a constructor does is returning the code of the contract.
+The gas costs for this depend on the length of the code and it might be
+that the supplied gas is not enough. This situation is the only one
+where an "out of gas" exception does not revert changes to the state,
+i.e. in this case the initialisation of the state variables.
+
+https://github.com/ethereum/wiki/wiki/Subtleties
+
+After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.
+
+
+How do I use .send()?
+=====================
+
+If you want to send 20 Ether from a contract to the address `x`, you use `x.send(20 ether);`.
+Here, `x` can be a plain address or a contract. If the contract already explicitly defines
+a function `send` (and thus overwrites the special function), you can use `address(x).send(20 ether);`.
+
+What does the following strange check do in the Custom Token contract?
+======================================================================
+
+::
+
+ if (balanceOf[_to] + _value < balanceOf[_to]) throw;
+
+Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range.
+For `uint256`, this is `0` up to `2**256 - 1`. If the result of some operation on those numbers
+does not fit inside this range, it is truncated. These truncations can have
+`serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_, so code like the one
+above is necessary to avoid certain attacks.
+
+
+More Questions?
+===============
+
+If you have more questions or your question is not answered here, please talk to us on
+`gitter <https://gitter.im/ethereum/solidity>`_ or file an `issue <https://github.com/ethereum/solidity/issues>`_.
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..fab886c7
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,66 @@
+Solidity
+========
+
+Solidity is a high-level language whose syntax is similar to that of JavaScript
+and it is designed to compile to code for the Ethereum Virtual Machine.
+As you will see, it is quite easy to create contracts for voting,
+crowdfunding, blind auctions, multi-signature wallets and more.
+
+.. note::
+ The best way to try out Solidity right now is using the
+ `Browser-Based Compiler <https://chriseth.github.io/browser-solidity/>`_
+ (it can take a while to load, please be patient).
+
+Useful links
+------------
+
+* `Ethereum <https://ethereum.org>`_
+
+* `Browser-Based Compiler <https://chriseth.github.io/browser-solidity/>`_
+
+* `Changelog <https://github.com/ethereum/wiki/wiki/Solidity-Changelog>`_
+
+* `Story Backlog <https://www.pivotaltracker.com/n/projects/1189488>`_
+
+* `Source Code <https://github.com/ethereum/solidity/>`_
+
+* `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
+
+* `Gitter Chat <https://gitter.im/ethereum/solidity/>`_
+
+Language Documentation
+----------------------
+
+On the next pages, we will first see a :ref:`simple smart contract <simple-smart-contract>` written
+in Solidity followed by the basics about :ref:`blockchains <blockchain-basics>`
+and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
+
+The next section will explain several *features* of Solidity by giving
+useful :ref:`example contracts <voting>`
+Remember that you can always try out the contracts
+`in your browser <https://chriseth.github.io/browser-solidity>`_!
+
+The last and most extensive section will cover all aspects of Solidity in depth.
+
+If you still have questions, you can try searching or asking on the
+`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
+site, or come to our `gitter channel <https://gitter.im/ethereum/solidity/>`_.
+Ideas for improving Solidity or this documentation are always welcome!
+
+See also `Russian version (русский перевод) <https://github.com/ethereum/wiki/wiki/%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_.
+
+Contents
+========
+
+:ref:`Keyword Index <genindex>`, :ref:`Search Page <search>`
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction-to-smart-contracts.rst
+ installing-solidity.rst
+ solidity-by-example.rst
+ solidity-in-depth.rst
+ style-guide.rst
+ common-patterns.rst
+ frequently-asked-questions.rst
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
new file mode 100644
index 00000000..af4bdcad
--- /dev/null
+++ b/docs/installing-solidity.rst
@@ -0,0 +1,151 @@
+###################
+Installing Solidity
+###################
+
+Browser-Solidity
+================
+
+If you just want to try Solidity for small contracts, you
+can try `browser-solidity <https://chriseth.github.io/browser-solidity>`_
+which does not need any installation. If you want to use it
+without connection to the Internet, you can also just save the page
+locally or clone http://github.com/chriseth/browser-solidity.
+
+NPM / node.js
+=============
+
+This is probably the most portable and most convenient way to install Solidity locally.
+
+A platform-independent JavaScript library is provided by compiling the C++ source
+into JavaScript using Emscripten for browser-solidity and there is also an NPM
+package available.
+
+To install it, simply use
+
+::
+
+ npm install solc
+
+Details about the usage of the nodejs package can be found in the
+`repository <https://github.com/chriseth/browser-solidity#nodejs-usage>`_.
+
+Binary Packages
+===============
+
+Binary packages of Solidity together with its IDE Mix are available through
+the `C++ bundle <https://github.com/ethereum/webthree-umbrella/releases>`_ of
+Ethereum.
+
+Building from Source
+====================
+
+Building Solidity is quite similar on MacOS X, Ubuntu and probably other Unices.
+This guide starts explaining how to install the dependencies for each platform
+and then shows how to build Solidity itself.
+
+MacOS X
+-------
+
+
+Requirements:
+
+- OS X Yosemite (10.10.5)
+- Homebrew
+- Xcode
+
+Set up Homebrew:
+
+.. code-block:: bash
+
+ brew update
+ brew install boost --c++11 # this takes a while
+ brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp
+ # For Mix IDE and Alethzero only
+ brew install xz d-bus
+ brew install homebrew/versions/v8-315
+ brew install llvm --HEAD --with-clang
+ brew install qt5 --with-d-bus # add --verbose if long waits with a stale screen drive you crazy as well
+
+Ubuntu
+------
+
+Below are the build instructions for the latest versions of Ubuntu. The best
+supported platform as of December 2014 is Ubuntu 14.04, 64 bit, with at least 2
+GB RAM. All our tests are done with this version. Community contributions for
+other versions are welcome!
+
+Install dependencies:
+
+Before you can build the source, you need several tools and dependencies for the application to get started.
+
+First, update your repositories. Not all packages are provided in the main
+Ubuntu repository, those you'll get from the Ethereum PPA and the LLVM archive.
+
+.. note::
+
+ Ubuntu 14.04 users, you'll need the latest version of cmake. For this, use:
+ `sudo apt-add-repository ppa:george-edison55/cmake-3.x`
+
+Now add all the rest:
+
+.. code-block:: bash
+
+ sudo apt-get -y update
+ sudo apt-get -y install language-pack-en-base
+ sudo dpkg-reconfigure locales
+ sudo apt-get -y install software-properties-common
+ sudo add-apt-repository -y ppa:ethereum/ethereum
+ sudo add-apt-repository -y ppa:ethereum/ethereum-dev
+ sudo apt-get -y update
+ sudo apt-get -y upgrade
+
+For Ubuntu 15.04 (Vivid Vervet) or older, use the following command to add the develop packages:
+
+.. code-block:: bash
+
+ sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
+
+For Ubuntu 15.10 (Wily Werewolf) or newer, use the following command instead:
+
+.. code-block:: bash
+
+ sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
+
+The reason for the change is that `libjsonrpccpp-dev` is available in the universe repository for newer versions of Ubuntu.
+
+Building
+--------
+
+Run this if you plan on installing Solidity only, ignore errors at the end as
+they relate only to Alethzero and Mix
+
+.. code-block:: bash
+
+ git clone --recursive https://github.com/ethereum/webthree-umbrella.git
+ cd webthree-umbrella
+ ./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity # update Solidity repo
+ ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0 # build Solidity and others
+ #enabling DEVMJIT on OS X will not build
+ #feel free to enable it on Linux
+
+If you opted to install Alethzero and Mix:
+
+.. code-block:: bash
+
+ git clone --recursive https://github.com/ethereum/webthree-umbrella.git
+ cd webthree-umbrella && mkdir -p build && cd build
+ cmake ..
+
+If you want to help developing Solidity,
+you should fork Solidity and add your personal fork as a second remote:
+
+.. code-block:: bash
+
+ cd webthree-umbrella/solidity
+ git remote add personal git@github.com:username/solidity.git
+
+Note that webthree-umbrella uses submodules, so solidity is its own git
+repository, but its settings are not stored in `.git/config`, but in
+`webthree-umbrella/.git/modules/solidity/config`.
+
+
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
new file mode 100644
index 00000000..22dbbcb7
--- /dev/null
+++ b/docs/introduction-to-smart-contracts.rst
@@ -0,0 +1,464 @@
+###############################
+Introduction to Smart Contracts
+###############################
+
+.. _simple-smart-contract:
+
+***********************
+A Simple Smart Contract
+***********************
+
+Let us begin with the most basic example. It is fine if you do not understand everything
+right now, we will go into more detail later.
+
+Storage
+=======
+
+.. Gist: a4532ce30246847b371b
+
+::
+
+ contract SimpleStorage {
+ uint storedData;
+ function set(uint x) {
+ storedData = x;
+ }
+ function get() constant returns (uint retVal) {
+ return storedData;
+ }
+ }
+
+A contract in the sense of Solidity is a collection of code (its functions) and
+data (its *state*) that resides at a specific address on the Ethereum
+blockchain. The line `uint storedData;` declares a state variable called `storedData` of
+type `uint` (unsigned integer of 256 bits). You can think of it as a single slot
+in a database that can be queried and altered by calling functions of the
+code that manages the database. In the case of Ethereum, this is always the owning
+contract. And in this case, the functions `set` and `get` can be used to modify
+or retrieve the value of the variable.
+
+To access a state variable, you do not need the prefix `this.` as is common in
+other languages.
+
+This contract does not yet do much apart from (due to the infrastructure
+built by Ethereum) allowing anyone to store a single number that is accessible by
+anyone in the world without (feasible) a way to prevent you from publishing
+this number. Of course, anyone could just call `set` again with a different value
+and overwrite your number, but the number will still be stored in the history
+of the blockchain. Later, we will see how you can impose access restrictions
+so that only you can alter the number.
+
+.. index:: ! subcurrency
+
+Subcurrency Example
+===================
+
+The following contract will implement the simplest form of a
+cryptocurrency. It is possible to generate coins out of thin air, but
+only the person that created the contract will be able to do that (it is trivial
+to implement a different issuance scheme).
+Furthermore, anyone can send coins to each other without any need for
+registering with username and password - all you need is an Ethereum keypair.
+
+
+.. note::
+ This is not a nice example for browser-solidity.
+ If you use `browser-solidity <https://chriseth.github.io/browser-solidity>`_
+ to try this example, you cannot change the address where you call
+ functions from. So you will always be the "minter", you can mint coins and send
+ them somewhere, but you cannot impersonate someone else. This might change in
+ the future.
+
+.. Gist: ad490694f3e5b3de47ab
+
+::
+
+ contract Coin {
+ // The keyword "public" makes those variables
+ // readable from outside.
+ address public minter;
+ mapping (address => uint) public balances;
+
+ // Events allow light clients to react on
+ // changes efficiently.
+ event Sent(address from, address to, uint amount);
+
+ // This is the constructor whose code is
+ // run only when the contract is created.
+ function Coin() {
+ minter = msg.sender;
+ }
+ function mint(address receiver, uint amount) {
+ if (msg.sender != minter) return;
+ balances[receiver] += amount;
+ }
+ function send(address receiver, uint amount) {
+ if (balances[msg.sender] < amount) return;
+ balances[msg.sender] -= amount;
+ balances[receiver] += amount;
+ Sent(msg.sender, receiver, amount);
+ }
+ }
+
+This contract introduces some new concepts, let us go through them one by one.
+
+The line `address public minter;` declares a state variable of type address
+that is publicly accessible. The `address` type is a 160 bit value
+that does not allow any arithmetic operations. It is suitable for
+storing addresses of contracts or keypairs belonging to external
+persons. The keyword `public` automatically generates a function that
+allows you to access the current value of the state variable.
+Without this keyword, other contracts have no way to access the variable
+and only the code of this contract can write to it.
+The function will look something like this::
+
+ function minter() returns (address) { return minter; }
+
+Of course, adding a function exactly like that will not work
+because we would have a
+function and a state variable with the same name, but hopefully, you
+get the idea - the compiler figures that out for you.
+
+.. index:: mapping
+
+The next line, `mapping (address => uint) public balances;` also
+creates a public state variable, but it of a more complex datatype.
+The type maps addresses to unsigned integers.
+Mappings can be seen as hashtables which are
+virtually initialized such that every possible key exists and is mapped to a
+value whose byte-representation is all zeros. This analogy does not go
+too far, though, as it is neither possible to obtain a list of all keys of
+a mapping, nor a list of all values. So either keep in mind (or
+better, keep a list or use a more advanced data type) what you
+added to the mapping or use it in a context where this is not needed,
+like this one. The accessor function created by the `public` keyword
+is a bit more complex in this case. It roughly looks like the
+following::
+
+ function balances(address _account) returns (uint balance) {
+ return balances[_account];
+ }
+
+As you see, you can use this function to easily query the balance of a
+single account.
+
+.. index:: event
+
+The line `event Sent(address from, address to, uint value);` declares
+a so-called "event" which is fired in the last line of the function
+`send`. User interfaces (as well as server appliances of course) can
+listen for those events being fired on the blockchain without much
+cost. As soon as it is fired, the listener will also receive the
+arguments `from`, `to` and `value`, which makes it easy to track
+transactions. In order to listen for this event, you would use ::
+
+ Coin.Sent().watch({}, '', function(error, result) {
+ if (!error) {
+ console.log("Coin transfer: " + result.args.amount +
+ " coins were sent from " + result.args.from +
+ " to " + result.args.to + ".");
+ console.log("Balances now:\n" +
+ "Sender: " + Coin.balances.call(result.args.from) +
+ "Receiver: " + Coin.balances.call(result.args.to));
+ }
+ }
+
+Note how the automatically generated function `balances` is called from
+the user interface.
+
+.. index:: coin
+
+The special function `Coin` is the
+constructor which is run during creation of the contract and
+cannot be called afterwards. It permanently stores the address of the person creating the
+contract: `msg` (together with `tx` and `block`) is a magic global variable that
+contains some properties which allow access to the blockchain. `msg.sender` is
+always the address where the current (external) function call came from.
+
+Finally, the functions that will actually end up with the contract and can be called
+by users and contracts alike are `mint` and `send`.
+If `mint` is called by anyone except the account that created the contract,
+nothing will happen. On the other hand, `send` can be used by anyone (who already
+has some of these coins) to send coins to anyone else. Note that if you use
+this contract to send coins to an address, you will not see anything when you
+look at that address on a blockchain explorer, because the fact that you sent
+coins and the changed balances are only stored in the data storage of this
+particular coin contract. By the use of events it is relatively easy to create
+a "blockchain explorer" that tracks transactions and balances of your new coin.
+
+.. _blockchain-basics:
+
+*****************
+Blockchain Basics
+*****************
+
+Blockchains as a concept are not too hard to understand for programmers. The reason is that
+most of the complications (mining, hashing, elliptic-curve cryptography, peer-to-peer networks, ...)
+are just there to provide a certain set of features and promises. Once you accept these
+features as given, you do not have to worry about the underlying technology - or do you have
+to know how Amazon's AWS works internally in order to use it?
+
+.. index:: transaction
+
+Transactions
+============
+
+A blockchain is a globally shared, transactional database.
+This means that everyone can read entries in the database just by participating in the network.
+If you want to change something in the database, you have to create a so-called transaction
+which has to be accepted by all others.
+The word transaction implies that the change you want to make (assume you want to change
+two values at the same time) is either not done at all or completely applied. Furthermore,
+while your transaction is applied to the database, no other transaction can alter it.
+
+As an example, imagine a table that lists the balances of all accounts in an
+electronic currency. If a transfer from one account to another is requested,
+the transactional nature of the database ensures that if the amount is
+subtracted from one account, it is always added to the other account. If due
+to whatever reason, adding the amount to the target account is not possible,
+the source account is also not modified.
+
+Furthermore, a transaction is always cryptographically signed by the sender (creator).
+This makes it straightforward to guard access to specific modifications of the
+database. In the example of the electronic currency, a simple check ensures that
+only the person holding the keys to the account can transfer money from it.
+
+.. index:: ! block
+
+Blocks
+======
+
+One major obstacle to overcome is what in bitcoin terms is called "double-spend attack":
+What happens if two transactions exist in the network that both want to empty an account,
+a so-called conflict?
+
+The abstract answer to this is that you do not have to care. An order of the transactions
+will be selected for you, the transactions will be bundled into what is called a "block"
+and then they will be executed and distributed among all participating nodes.
+If two transactions contradict each other, the one that ends up being second will
+be rejected and not become part of the block.
+
+These blocks form a linear sequence in time and that is where the word "blockchain"
+derives from. Blocks are added to the chain in rather regular intervals - for
+Ethereum this is roughly every 17 seconds.
+
+As part of the "order selection mechanism" (which is called "mining") it may happen that
+blocks are reverted from time to time, but only at the "tip" of the chain. The more
+blocks are reverted the less likely it is. So it might be that your transactions
+are reverted and even removed from the blockchain, but the longer you wait, the less
+likely it will be.
+
+
+.. _the-ethereum-virtual-machine:
+
+.. index:: !evm, ! ethereum virtual machine
+
+****************************
+The Ethereum Virtual Machine
+****************************
+
+Overview
+========
+
+The Ethereum Virtual Machine or EVM is the runtime environment
+for smart contracts in Ethereum. It is not only sandboxed but
+actually completely isolated, which means that code running
+inside the EVM has no access to network, filesystem or other processes.
+Smart contracts even have limited access to other smart contracts.
+
+.. index:: ! account, address, storage, balance
+
+Accounts
+========
+
+There are two kinds of accounts in Ethereum which share the same
+address space: **External accounts** that are controlled by
+public-private key pairs (i.e. humans) and **contract accounts** which are
+controlled by the code stored together with the account.
+
+The address of an external account is determined from
+the public key while the address of a contract is
+determined at the time the contract is created
+(it is derived from the creator address and the number
+of transactions sent from that address, the so-called "nonce").
+
+Apart from the fact whether an account stores code or not,
+the EVM treats the two types equally, though.
+
+Every account has a persistent key-value store mapping 256 bit words to 256 bit
+words called **storage**.
+
+Furthermore, every account has a **balance** in
+Ether (in "Wei" to be exact) which can be modified by sending transactions that
+include Ether.
+
+.. index:: ! transaction
+
+Transactions
+============
+
+A transaction is a message that is sent from one account to another
+account (which might be the same or the special zero-account, see below).
+It can include binary data (its payload) and Ether.
+
+If the target account contains code, that code is executed and
+the payload is provided as input data.
+
+If the target account is the zero-account (the account with the
+address `0`), the transaction creates a **new contract**.
+As already mentioned, the address of that contract is not
+the zero address but an address derived from the sender and
+its number of transaction sent (the "nonce"). The payload
+of such a contract creation transaction is taken to be
+EVM bytecode and executed. The output of this execution is
+permanently stored as the code of the contract.
+This means that in order to create a contract, you do not
+send the actual code of the contract, but in fact code that
+returns that code.
+
+.. index:: ! gas, ! gas price
+
+Gas
+===
+
+Upon creation, each transaction is charged with a certain amount of **gas**,
+whose purpose is to limit the amount of work that is needed to execute
+the transaction and to pay for this execution. While the EVM executes the
+transaction, the gas is gradually depleted according to specific rules.
+
+The **gas price** is a value set by the creator of the transaction, who
+has to pay `gas_price * gas` up front from the sending account.
+If some gas is left after the execution, it is refunded in the same way.
+
+If the gas is used up at any point (i.e. it is negative),
+an out-of-gas exception is triggered, which reverts all modifications
+made to the state in the current call frame.
+
+.. index:: ! storage, ! memory, ! stack
+
+Storage, Memory and the Stack
+=============================
+
+Each account has a persistent memory area which is called **storage**.
+Storage is a key-value store that maps 256 bit words to 256 bit words.
+It is not possible to enumerate storage from within a contract
+and it is comparatively costly to read and even more so, to modify
+storage. A contract can neither read nor write to any storage apart
+from its own.
+
+The second memory area is called **memory**, of which a contract obtains
+a freshly cleared instance for each message call. Memory can be
+addressed at byte level, but read and written to in 32 byte (256 bit)
+chunks. Memory is more costly the larger it grows (it scales
+quadratically).
+
+The EVM is not a register machine but a stack machine, so all
+computations are performed on an area called the **stack**. It has a maximum size of
+1024 elements and contains words of 256 bits. Access to the stack is
+limited to the top end in the following way:
+It is possible to copy one of
+the topmost 16 elements to the top of the stack or swap the
+topmost element with one of the 16 elements below it.
+All other operations take the topmost two (or one, or more, depending on
+the operation) elements from the stack and push the result onto the stack.
+Of course it is possible to move stack elements to storage or memory,
+but it is not possible to just access arbitrary elements deeper in the stack
+without first removing the top of the stack.
+
+.. index:: ! instruction
+
+Instruction Set
+===============
+
+The instruction set of the EVM is kept minimal in order to avoid
+incorrect implementations which could cause consensus problems.
+All instructions operate on the basic data type, 256 bit words.
+The usual arithmetic, bit, logical and comparison operations are present.
+Conditional and unconditional jumps are possible. Furthermore,
+contracts can access relevant properties of the current block
+like its number and timestamp.
+
+.. index:: ! message call, function;call
+
+Message Calls
+=============
+
+Contracts can call other contracts or send Ether to non-contract
+accounts by the means of message calls. Message calls are similar
+to transactions, in that they have a source, a target, data payload,
+Ether, gas and return data. In fact, every transaction consists of
+a top-level message call which in turn can create further message calls.
+
+A contract can decide how much of its remaining **gas** should be sent
+with the inner message call and how much it wants to retain.
+If an out-of-gas exception happens in the inner call (or any
+other exception), this will be signalled by an error value put onto the stack.
+In this case, only the gas sent together with the call is used up.
+In Solidity, the calling contract causes a manual exception by default in
+such situations, so that exceptions "bubble up" the call stack.
+
+As already said, the called contract (which can be the same as the caller)
+will receive a freshly cleared instance of memory and has access to the
+call payload - which will be provided in a separate area called the **calldata**.
+After it finished execution, it can return data which will be stored at
+a location in the caller's memory preallocated by the caller.
+
+Calls are **limited** to a depth of 1024, which means that for more complex
+operations, loops should be preferred over recursive calls.
+
+.. index:: delegatecall, callcode, library
+
+Delegatecall / Callcode and Libraries
+=====================================
+
+There exists a special variant of a message call, named **delegatecall**
+which is identical to a message call apart from the fact that
+the code at the target address is executed in the context of the calling
+contract and `msg.sender` and `msg.value` do not change their values.
+
+This means that a contract can dynamically load code from a different
+address at runtime. Storage, current address and balance still
+refer to the calling contract, only the code is taken from the called address.
+
+This makes it possible to implement the "library" feature in Solidity:
+Reusable library code that can be applied to a contract's storage in
+order to e.g. implement a complex data structure.
+
+.. index:: log
+
+Logs
+====
+
+It is possible to store data in a specially indexed data structure
+that maps all they way up to the block level. This feature called **logs**
+is used by Solidity in order to implement **events**.
+Contracts cannot access log data after it has been created, but they
+can be efficiently accessed from outside the blockchain.
+Since some part of the log data is stored in bloom filters, it is
+possible to search for this data in an efficient and cryptographically
+secure way, so network peers that do not download the whole blockchain
+("light clients") can still find these logs.
+
+.. index:: contract creation
+
+Create
+======
+
+Contracts can even create other contracts using a special opcode (i.e.
+they do not simply call the zero address). The only difference between
+these **create calls** and normal message calls is that the payload data is
+executed and the result stored as code and the caller / creator
+receives the address of the new contract on the stack.
+
+.. index:: selfdestruct
+
+Selfdestruct
+============
+
+The only possibility that code is removed from the blockchain is
+when a contract at that address performs the `SELFDESTRUCT` operation.
+The remaining Ether stored at that address is sent to a designated
+target and then the storage and code is removed.
+
+Note that even if a contract's code does not contain the `SELFDESTRUCT`
+opcode, it can still perform that operation using delegatecall or callcode.
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
new file mode 100644
index 00000000..48ecfb09
--- /dev/null
+++ b/docs/layout-of-source-files.rst
@@ -0,0 +1,138 @@
+********************************
+Layout of a Solidity Source File
+********************************
+
+Source files can contain an arbitrary number of contract definitions and include directives.
+
+.. index:: source file, ! import
+
+Importing other Source Files
+============================
+
+Syntax and Semantics
+--------------------
+
+Solidity supports import statements that are very similar to those available in JavaScript
+(from ES6 on), although Solidity does not know the concept of a "default export".
+
+At a global level, you can use import statements of the following form:
+
+::
+
+ import "filename";
+
+...will import all global symbols from "filename" (and symbols imported there) into the
+current global scope (different than in ES6 but backwards-compatible for Solidity).
+
+::
+
+ import * as symbolName from "filename";
+
+...creates a new global symbol `symbolName` whose members are all the global symbols from `"filename"`.
+
+::
+
+ import {symbol1 as alias, symbol2} from "filename";
+
+...creates new global symbols `alias` and `symbol2` which reference `symbol1` and `symbol2` from `"filename"`, respectively.
+
+Another syntax is not part of ES6, but probably convenient:
+
+::
+
+ import "filename" as symbolName;
+
+...is equivalent to `import * as symbolName from "filename";`.
+
+Paths
+-----
+
+In the above, `filename` is always treated as a path with `/` as directory separator,
+`.` as the current and `..` as the parent directory. Path names that do not start
+with `.` are treated as absolute paths.
+
+To import a file `x` from the same directory as the current file, use `import "./x" as x;`.
+If you use `import "x" as x;` instead, a different file could be referenced
+(in a global "include directory").
+
+It depends on the compiler (see below) how to actually resolve the paths.
+In general, the directory hierarchy does not need to strictly map onto your local
+filesystem, it can also map to resources discovered via e.g. ipfs, http or git.
+
+Use in actual Compilers
+-----------------------
+
+When the compiler is invoked, it is not only possible to specify how to
+discover the first element of a path, but it is possible to specify path prefix
+remappings so that e.g. `github.com/ethereum/dapp-bin/library` is remapped to
+`/usr/local/dapp-bin/library` and the compiler will read the files from there. If
+remapping keys are prefixes of each other, the longest is tried first. This
+allows for a "fallback-remapping" with e.g. `""` maps to
+`"/usr/local/include/solidity"`.
+
+**solc**:
+
+For solc (the commandline compiler), these remappings are provided as `key=value`
+arguments, where the `=value` part is optional (and defaults to key in that
+case). All remapping values that are regular files are compiled (including
+their dependencies). This mechanism is completely backwards-compatible (as long
+as no filename contains a =) and thus not a breaking change.
+
+So as an example, if you clone
+`github.com/ethereum/dapp-bin/` locally to `/usr/local/dapp-bin`, you can use
+the following in your source file:
+
+::
+
+ import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
+
+and then run the compiler as
+
+.. code-block:: shell
+
+ solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
+
+Note that solc only allows you to include files from certain directories:
+They have to be in the directory (or subdirectory) of one of the explicitly
+specified source files or in the directory (or subdirectory) of a remapping
+target. If you want to allow direct absolute includes, just add the
+remapping `=/`.
+
+If there are multiple remappings that lead to a valid file, the remapping
+with the longest common prefix is chosen.
+
+**browser-solidity**:
+
+The `browser-based compiler <https://chriseth.github.io/browser-solidity>`_
+provides an automatic remapping for github and will also automatically retrieve
+the file over the network:
+You can import the iterable mapping by e.g.
+`import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;`.
+
+Other source code providers may be added in the future.
+
+
+.. index:: ! comment, natspec
+
+Comments
+========
+
+Single-line comments (`//`) and multi-line comments (`/*...*/`) are possible.
+
+::
+
+ // This is a single-line comment.
+
+ /*
+ This is a
+ multi-line comment.
+ */
+
+
+There are special types of comments called natspec comments
+(documentation yet to be written). These are introduced by
+triple-slash comments (`///`) or using double asterisks (`/** ... */`).
+Right in front of function declarations or statements,
+you can use doxygen-style tags inside them to document functions, annotate conditions for formal
+verification and provide a **confirmation text** that is shown to users if they want to
+invoke a function.
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..de2bccf7
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,242 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Solidity.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Solidity.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
new file mode 100644
index 00000000..f2ad0f88
--- /dev/null
+++ b/docs/miscellaneous.rst
@@ -0,0 +1,206 @@
+#############
+Miscellaneous
+#############
+
+.. index:: storage, state variable, mapping
+
+************************************
+Layout of State Variables in Storage
+************************************
+
+Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position `0`. Multiple items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
+
+- The first item in a storage slot is stored lower-order aligned.
+- Elementary types use only that many bytes that are necessary to store them.
+- If an elementary type does not fit the remaining part of a storage slot, it is moved to the next storage slot.
+- Structs and array data always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules).
+
+The elements of structs and arrays are stored after each other, just as if they were given explicitly.
+
+Due to their unpredictable size, mapping and dynamically-sized array types use a `sha3`
+computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
+
+The mapping or the dynamic array itself
+occupies an (unfilled) slot in storage at some position `p` according to the above rule (or by
+recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution).
+Array data is located at `sha3(p)` and the value corresponding to a mapping key
+`k` is located at `sha3(k . p)` where `.` is concatenation. If the value is again a
+non-elementary type, the positions are found by adding an offset of `sha3(k . p)`.
+
+`bytes` and `string` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most `31` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores `length * 2`. If it is longer, the main slot stores `length * 2 + 1` and the data is stored as usual in `sha3(slot)`.
+
+So for the following contract snippet::
+
+ contract c {
+ struct S { uint a; uint b; }
+ uint x;
+ mapping(uint => mapping(uint => S)) data;
+ }
+
+The position of `data[4][9].b` is at `sha3(uint256(9) . sha3(uint256(4) . uint256(1))) + 1`.
+
+*****************
+Esoteric Features
+*****************
+
+There are some types in Solidity's type system that have no counterpart in the syntax. One of these types are the types of functions. But still, using `var` it is possible to have local variables of these types::
+
+ contract FunctionSelector {
+ function select(bool useB, uint x) returns (uint z) {
+ var f = a;
+ if (useB) f = b;
+ return f(x);
+ }
+ function a(uint x) returns (uint z) {
+ return x * x;
+ }
+ function b(uint x) returns (uint z) {
+ return 2 * x;
+ }
+ }
+
+Calling `select(false, x)` will compute `x * x` and `select(true, x)` will compute `2 * x`.
+
+.. index:: optimizer, common subexpression elimination, constant propagation
+
+*************************
+Internals - the Optimizer
+*************************
+
+The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at JUMPs and JUMPDESTs. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like `constant` + `constant` = `sum_of_constants` or `X` * 1 = `X`. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different: If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we actually do not know what is stored at x after we wrote to y. On the other hand, if a simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x.
+
+At the end of this process, we know which expressions have to be on the stack in the end and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all JUMP and JUMPI instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown JUMP. If a JUMPI is found whose condition evaluates to a constant, it is transformed to an unconditional jump.
+
+As the last step, the code in each block is completely re-generated. A dependency graph is created from the expressions on the stack at the end of the block and every operation that is not part of this graph is essentially dropped. Now code is generated that applies the modifications to memory and storage in the order they were made in the original code (dropping modifications which were found not to be needed) and finally, generates all values that are required to be on the stack in the correct place.
+
+These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a JUMPI and during the analysis, the condition evaluates to a constant, the JUMPI is replaced depending on the value of the constant, and thus code like
+
+::
+
+ var x = 7;
+ data[7] = 9;
+ if (data[x] != x + 2)
+ return 2;
+ else
+ return 1;
+
+is simplified to code which can also be compiled from
+
+::
+
+ data[7] = 9;
+ return 1;
+
+even though the instructions contained a jump in the beginning.
+
+.. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker
+
+******************************
+Using the Commandline Compiler
+******************************
+
+One of the build targets of the Solidity repository is `solc`, the solidity commandline compiler.
+Using `solc --help` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
+If you only want to compile a single file, you run it as `solc --bin sourceFile.sol` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using `solc --optimize --bin sourceFile.sol`. If you want to get some of the more advanced output variants of `solc`, it is probably better to tell it to output everything to separate files using `solc -o outputDirectory --bin --ast --asm sourceFile.sol`.
+
+The commandline compiler will automatically read imported files from the filesystem, but
+it is also possible to provide path redirects using `prefix=path` in the following way:
+
+ solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
+
+This essentially instructs the compiler to search for anything starting with
+`github.com/ethereum/dapp-bin/` under `/usr/local/lib/dapp-bin` and if it does not
+find the file there, it will look at `/usr/local/lib/fallback` (the empty prefix
+always matches). `solc` will not read files from the filesystem that lie outside of
+the remapping targets and outside of the directories where explicitly specified source
+files reside, so things like `import "/etc/passwd";` only work if you add `=/` as a remapping.
+
+If there are multiple matches due to remappings, the one with the longest common prefix is selected.
+
+If your contracts use [libraries](#libraries), you will notice that the bytecode contains substrings of the form `__LibraryName______`. You can use `solc` as a linker meaning that it will insert the library addresses for you at those points:
+
+Either add `--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"` to your command to provide an address for each library or store the string in a file (one library per line) and run `solc` using `--libraries fileName`.
+
+If `solc` is called with the option `--link`, all input files are interpreted to be unlinked binaries (hex-encoded) in the `__LibraryName____`-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except `--libraries` are ignored (including `-o`) in this case.
+
+***************
+Tips and Tricks
+***************
+
+* Use `delete` on arrays to delete all its elements.
+* Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple SSTORE operations might be combined into a single (SSTORE costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check!
+* Make your state variables public - the compiler will create :ref:`getters <visibility-and-accessors>` for you for free.
+* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
+* If your contract has a function called `send` but you want to use the built-in send-function, use `address(contractVariable).send(amount)`.
+* If you do **not** want your contracts to receive ether when called via `send`, you can add a throwing fallback function `function() { throw; }`.
+* Initialise storage structs with a single assignment: `x = MyStruct({a: 1, b: 2});`
+
+********
+Pitfalls
+********
+
+Unfortunately, there are some subtleties the compiler does not yet warn you about.
+
+- In `for (var i = 0; i < arrayName.length; i++) { ... }`, the type of `i` will be `uint8`, because this is the smallest type that is required to hold the value `0`. If the array has more than 255 elements, the loop will not terminate.
+
+**********
+Cheatsheet
+**********
+
+.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, sha3, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
+
+Global Variables
+================
+
+- `block.coinbase` (`address`): current block miner's address
+- `block.difficulty` (`uint`): current block difficulty
+- `block.gaslimit` (`uint`): current block gaslimit
+- `block.number` (`uint`): current block number
+- `block.blockhash` (`function(uint) returns (bytes32)`): hash of the given block - only works for 256 most recent blocks
+- `block.timestamp` (`uint`): current block timestamp
+- `msg.data` (`bytes`): complete calldata
+- `msg.gas` (`uint`): remaining gas
+- `msg.sender` (`address`): sender of the message (current call)
+- `msg.value` (`uint`): number of wei sent with the message
+- `now` (`uint`): current block timestamp (alias for `block.timestamp`)
+- `tx.gasprice` (`uint`): gas price of the transaction
+- `tx.origin` (`address`): sender of the transaction (full call chain)
+- `sha3(...) returns (bytes32)`: compute the Ethereum-SHA3 hash of the (tightly packed) arguments
+- `sha256(...) returns (bytes32)`: compute the SHA256 hash of the (tightly packed) arguments
+- `ripemd160(...) returns (bytes20)`: compute RIPEMD of 256 the (tightly packed) arguments
+- `ecrecover(bytes32, uint8, bytes32, bytes32) returns (address)`: recover public key from elliptic curve signature
+- `addmod(uint x, uint y, uint k) returns (uint)`: compute `(x + y) % k` where the addition is performed with arbitrary precision and does not wrap around at `2**256`.
+- `mulmod(uint x, uint y, uint k) returns (uint)`: compute `(x * y) % k` where the multiplication is performed with arbitrary precision and does not wrap around at `2**256`.
+- `this` (current contract's type): the current contract, explicitly convertible to `address`
+- `super`: the contract one level higher in the inheritance hierarchy
+- `selfdestruct(address)`: destroy the current contract, sending its funds to the given address
+- `<address>.balance`: balance of the address in Wei
+- `<address>.send(uint256) returns (bool)`: send given amount of Wei to address, returns `false` on failure.
+
+.. index:: visibility, public, private, external, internal
+
+Function Visibility Specifiers
+==============================
+
+::
+
+ function myFunction() <visibility specifier> returns (bool) {
+ return true;
+ }
+
+- `public`: visible externally and internally (creates accessor function for storage/state variables)
+- `private`: only visible in the current contract
+- `external`: only visible externally (only for functions) - i.e. can only be message-called (via `this.fun`)
+- `internal`: only visible internally
+
+
+.. index:: modifiers, constant, anonymous, indexed
+
+Modifiers
+=========
+
+- `constant` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
+- `constant` for functions: Disallows modification of state - this is not enforced yet.
+- `anonymous` for events: Does not store event signature as topic.
+- `indexed` for event parameters: Stores the parameter as topic.
+
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
new file mode 100644
index 00000000..63c82798
--- /dev/null
+++ b/docs/solidity-by-example.rst
@@ -0,0 +1,530 @@
+###################
+Solidity by Example
+###################
+
+.. index:: voting, ballot
+
+.. _voting:
+
+******
+Voting
+******
+
+The following contract is quite complex, but showcases
+a lot of Solidity's features. It implements a voting
+contract. Of course, the main problems of electronic
+voting is how to assign voting rights to the correct
+persons and how to prevent manipulation. We will not
+solve all problems here, but at least we will show
+how delegated voting can be done so that vote counting
+is **automatic and completely transparent** at the
+same time.
+
+The idea is to create one contract per ballot,
+providing a short name for each option.
+Then the creator of the contract who serves as
+chairperson will give the right to vote to each
+address individually.
+
+The persons behind the addresses can then choose
+to either vote themselves or to delegate their
+vote to a person they trust.
+
+At the end of the voting time, `winningProposal()`
+will return the proposal with the largest number
+of votes.
+
+.. Gist: 618560d3f740204d46a5
+
+::
+
+ /// @title Voting with delegation.
+ contract Ballot
+ {
+ // This declares a new complex type which will
+ // be used for variables later.
+ // It will represent a single voter.
+ struct Voter
+ {
+ uint weight; // weight is accumulated by delegation
+ bool voted; // if true, that person already voted
+ address delegate; // person delegated to
+ uint vote; // index of the voted proposal
+ }
+ // This is a type for a single proposal.
+ struct Proposal
+ {
+ bytes32 name; // short name (up to 32 bytes)
+ uint voteCount; // number of accumulated votes
+ }
+
+ address public chairperson;
+ // This declares a state variable that
+ // stores a `Voter` struct for each possible address.
+ mapping(address => Voter) public voters;
+ // A dynamically-sized array of `Proposal` structs.
+ Proposal[] public proposals;
+
+ /// Create a new ballot to choose one of `proposalNames`.
+ function Ballot(bytes32[] proposalNames)
+ {
+ chairperson = msg.sender;
+ voters[chairperson].weight = 1;
+ // For each of the provided proposal names,
+ // create a new proposal object and add it
+ // to the end of the array.
+ for (uint i = 0; i < proposalNames.length; i++)
+ // `Proposal({...})` creates a temporary
+ // Proposal object and `proposal.push(...)`
+ // appends it to the end of `proposals`.
+ proposals.push(Proposal({
+ name: proposalNames[i],
+ voteCount: 0
+ }));
+ }
+
+ // Give `voter` the right to vote on this ballot.
+ // May only be called by `chairperson`.
+ function giveRightToVote(address voter)
+ {
+ if (msg.sender != chairperson || voters[voter].voted)
+ // `throw` terminates and reverts all changes to
+ // the state and to Ether balances. It is often
+ // a good idea to use this if functions are
+ // called incorrectly. But watch out, this
+ // will also consume all provided gas.
+ throw;
+ voters[voter].weight = 1;
+ }
+
+ /// Delegate your vote to the voter `to`.
+ function delegate(address to)
+ {
+ // assigns reference
+ Voter sender = voters[msg.sender];
+ if (sender.voted)
+ throw;
+ // Forward the delegation as long as
+ // `to` also delegated.
+ while (voters[to].delegate != address(0) &&
+ voters[to].delegate != msg.sender)
+ to = voters[to].delegate;
+ // We found a loop in the delegation, not allowed.
+ if (to == msg.sender)
+ throw;
+ // Since `sender` is a reference, this
+ // modifies `voters[msg.sender].voted`
+ sender.voted = true;
+ sender.delegate = to;
+ Voter delegate = voters[to];
+ if (delegate.voted)
+ // If the delegate already voted,
+ // directly add to the number of votes
+ proposals[delegate.vote].voteCount += sender.weight;
+ else
+ // If the delegate did not vote yet,
+ // add to her weight.
+ delegate.weight += sender.weight;
+ }
+
+ /// Give your vote (including votes delegated to you)
+ /// to proposal `proposals[proposal].name`.
+ function vote(uint proposal)
+ {
+ Voter sender = voters[msg.sender];
+ if (sender.voted) throw;
+ sender.voted = true;
+ sender.vote = proposal;
+ // If `proposal` is out of the range of the array,
+ // this will throw automatically and revert all
+ // changes.
+ proposals[proposal].voteCount += sender.weight;
+ }
+
+ /// @dev Computes the winning proposal taking all
+ /// previous votes into account.
+ function winningProposal() constant
+ returns (uint winningProposal)
+ {
+ uint winningVoteCount = 0;
+ for (uint p = 0; p < proposals.length; p++)
+ {
+ if (proposals[p].voteCount > winningVoteCount)
+ {
+ winningVoteCount = proposals[p].voteCount;
+ winningProposal = p;
+ }
+ }
+ }
+ }
+
+Possible Improvements
+=====================
+
+Currently, many transactions are needed to assign the rights
+to vote to all participants. Can you think of a better way?
+
+.. index:: auction;blind, auction;open, blind auction, open auction
+
+*************
+Blind Auction
+*************
+
+In this section, we will show how easy it is to create a
+completely blind auction contract on Ethereum.
+We will start with an open auction where everyone
+can see the bids that are made and then extend this
+contract into a blind auction where it is not
+possible to see the actual bid until the bidding
+period ends.
+
+Simple Open Auction
+===================
+
+The general idea of the following simple auction contract
+is that everyone can send their bids during
+a bidding period. The bids already include sending
+money / ether in order to bind the bidders to their
+bid. If the highest bid is raised, the previously
+highest bidder gets her money back.
+After the end of the bidding period, the
+contract has to be called manually for the
+beneficiary to receive his money - contracts cannot
+activate themselves.
+
+.. {% include open_link gist="48cd2b65ff83bd04f7af" %}
+
+::
+
+ contract SimpleAuction {
+ // Parameters of the auction. Times are either
+ // absolute unix timestamps (seconds since 1970-01-01)
+ // ore time periods in seconds.
+ address public beneficiary;
+ uint public auctionStart;
+ uint public biddingTime;
+
+ // Current state of the auction.
+ address public highestBidder;
+ uint public highestBid;
+
+ // Set to true at the end, disallows any change
+ bool ended;
+
+ // Events that will be fired on changes.
+ event HighestBidIncreased(address bidder, uint amount);
+ event AuctionEnded(address winner, uint amount);
+
+ // The following is a so-called natspec comment,
+ // recognizable by the three slashes.
+ // It will be shown when the user is asked to
+ // confirm a transaction.
+
+ /// Create a simple auction with `_biddingTime`
+ /// seconds bidding time on behalf of the
+ /// beneficiary address `_beneficiary`.
+ function SimpleAuction(uint _biddingTime,
+ address _beneficiary) {
+ beneficiary = _beneficiary;
+ auctionStart = now;
+ biddingTime = _biddingTime;
+ }
+
+ /// Bid on the auction with the value sent
+ /// together with this transaction.
+ /// The value will only be refunded if the
+ /// auction is not won.
+ function bid() {
+ // No arguments are necessary, all
+ // information is already part of
+ // the transaction.
+ if (now > auctionStart + biddingTime)
+ // Revert the call if the bidding
+ // period is over.
+ throw;
+ if (msg.value <= highestBid)
+ // If the bid is not higher, send the
+ // money back.
+ throw;
+ if (highestBidder != 0)
+ highestBidder.send(highestBid);
+ highestBidder = msg.sender;
+ highestBid = msg.value;
+ HighestBidIncreased(msg.sender, msg.value);
+ }
+
+ /// End the auction and send the highest bid
+ /// to the beneficiary.
+ function auctionEnd() {
+ if (now <= auctionStart + biddingTime)
+ throw; // auction did not yet end
+ if (ended)
+ throw; // this function has already been called
+ AuctionEnded(highestBidder, highestBid);
+ // We send all the money we have, because some
+ // of the refunds might have failed.
+ beneficiary.send(this.balance);
+ ended = true;
+ }
+
+ function () {
+ // This function gets executed if a
+ // transaction with invalid data is sent to
+ // the contract or just ether without data.
+ // We revert the send so that no-one
+ // accidentally loses money when using the
+ // contract.
+ throw;
+ }
+ }
+
+Blind Auction
+================
+
+The previous open auction is extended to a blind auction
+in the following. The advantage of a blind auction is
+that there is no time pressure towards the end of
+the bidding period. Creating a blind auction on a
+transparent computing platform might sound like a
+contradiction, but cryptography comes to the rescue.
+
+During the **bidding period**, a bidder does not
+actually send her bid, but only a hashed version of it.
+Since it is currently considered practically impossible
+to find two (sufficiently long) values whose hash
+values are equal, the bidder commits to the bid by that.
+After the end of the bidding period, the bidders have
+to reveal their bids: They send their values
+unencrypted and the contract checks that the hash value
+is the same as the one provided during the bidding period.
+
+Another challenge is how to make the auction
+**binding and blind** at the same time: The only way to
+prevent the bidder from just not sending the money
+after he won the auction is to make her send it
+together with the bid. Since value transfers cannot
+be blinded in Ethereum, anyone can see the value.
+
+The following contract solves this problem by
+accepting any value that is at least as large as
+the bid. Since this can of course only be checked during
+the reveal phase, some bids might be **invalid**, and
+this is on purpose (it even provides an explicit
+flag to place invalid bids with high value transfers):
+Bidders can confuse competition by placing several
+high or low invalid bids.
+
+
+.. {% include open_link gist="70528429c2cd867dd1d6" %}
+
+::
+
+ contract BlindAuction
+ {
+ struct Bid
+ {
+ bytes32 blindedBid;
+ uint deposit;
+ }
+ address public beneficiary;
+ uint public auctionStart;
+ uint public biddingEnd;
+ uint public revealEnd;
+ bool public ended;
+
+ mapping(address => Bid[]) public bids;
+
+ address public highestBidder;
+ uint public highestBid;
+
+ event AuctionEnded(address winner, uint highestBid);
+
+ /// Modifiers are a convenient way to validate inputs to
+ /// functions. `onlyBefore` is applied to `bid` below:
+ /// The new function body is the modifier's body where
+ /// `_` is replaced by the old function body.
+ modifier onlyBefore(uint _time) { if (now >= _time) throw; _ }
+ modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }
+
+ function BlindAuction(uint _biddingTime,
+ uint _revealTime,
+ address _beneficiary)
+ {
+ beneficiary = _beneficiary;
+ auctionStart = now;
+ biddingEnd = now + _biddingTime;
+ revealEnd = biddingEnd + _revealTime;
+ }
+
+ /// Place a blinded bid with `_blindedBid` = sha3(value,
+ /// fake, secret).
+ /// The sent ether is only refunded if the bid is correctly
+ /// revealed in the revealing phase. The bid is valid if the
+ /// ether sent together with the bid is at least "value" and
+ /// "fake" is not true. Setting "fake" to true and sending
+ /// not the exact amount are ways to hide the real bid but
+ /// still make the required deposit. The same address can
+ /// place multiple bids.
+ function bid(bytes32 _blindedBid)
+ onlyBefore(biddingEnd)
+ {
+ bids[msg.sender].push(Bid({
+ blindedBid: _blindedBid,
+ deposit: msg.value
+ }));
+ }
+
+ /// Reveal your blinded bids. You will get a refund for all
+ /// correctly blinded invalid bids and for all bids except for
+ /// the totally highest.
+ function reveal(uint[] _values, bool[] _fake,
+ bytes32[] _secret)
+ onlyAfter(biddingEnd)
+ onlyBefore(revealEnd)
+ {
+ uint length = bids[msg.sender].length;
+ if (_values.length != length || _fake.length != length ||
+ _secret.length != length)
+ throw;
+ uint refund;
+ for (uint i = 0; i < length; i++)
+ {
+ var bid = bids[msg.sender][i];
+ var (value, fake, secret) =
+ (_values[i], _fake[i], _secret[i]);
+ if (bid.blindedBid != sha3(value, fake, secret))
+ // Bid was not actually revealed.
+ // Do not refund deposit.
+ continue;
+ refund += bid.deposit;
+ if (!fake && bid.deposit >= value)
+ if (placeBid(msg.sender, value))
+ refund -= value;
+ // Make it impossible for the sender to re-claim
+ // the same deposit.
+ bid.blindedBid = 0;
+ }
+ msg.sender.send(refund);
+ }
+
+ // This is an "internal" function which means that it
+ // can only be called from the contract itself (or from
+ // derived contracts).
+ function placeBid(address bidder, uint value) internal
+ returns (bool success)
+ {
+ if (value <= highestBid)
+ return false;
+ if (highestBidder != 0)
+ // Refund the previously highest bidder.
+ highestBidder.send(highestBid);
+ highestBid = value;
+ highestBidder = bidder;
+ return true;
+ }
+
+ /// End the auction and send the highest bid
+ /// to the beneficiary.
+ function auctionEnd()
+ onlyAfter(revealEnd)
+ {
+ if (ended) throw;
+ AuctionEnded(highestBidder, highestBid);
+ // We send all the money we have, because some
+ // of the refunds might have failed.
+ beneficiary.send(this.balance);
+ ended = true;
+ }
+
+ function () { throw; }
+ }
+
+.. index:: purchase, remote purchase, escrow
+
+********************
+Safe Remote Purchase
+********************
+
+.. {% include open_link gist="b16e8e76a423b7671e99" %}
+
+::
+
+ contract Purchase
+ {
+ uint public value;
+ address public seller;
+ address public buyer;
+ enum State { Created, Locked, Inactive }
+ State public state;
+ function Purchase()
+ {
+ seller = msg.sender;
+ value = msg.value / 2;
+ if (2 * value != msg.value) throw;
+ }
+ modifier require(bool _condition)
+ {
+ if (!_condition) throw;
+ _
+ }
+ modifier onlyBuyer()
+ {
+ if (msg.sender != buyer) throw;
+ _
+ }
+ modifier onlySeller()
+ {
+ if (msg.sender != seller) throw;
+ _
+ }
+ modifier inState(State _state)
+ {
+ if (state != _state) throw;
+ _
+ }
+ event aborted();
+ event purchaseConfirmed();
+ event itemReceived();
+
+ /// Abort the purchase and reclaim the ether.
+ /// Can only be called by the seller before
+ /// the contract is locked.
+ function abort()
+ onlySeller
+ inState(State.Created)
+ {
+ aborted();
+ seller.send(this.balance);
+ state = State.Inactive;
+ }
+ /// Confirm the purchase as buyer.
+ /// Transaction has to include `2 * value` ether.
+ /// The ether will be locked until confirmReceived
+ /// is called.
+ function confirmPurchase()
+ inState(State.Created)
+ require(msg.value == 2 * value)
+ {
+ purchaseConfirmed();
+ buyer = msg.sender;
+ state = State.Locked;
+ }
+ /// Confirm that you (the buyer) received the item.
+ /// This will release the locked ether.
+ function confirmReceived()
+ onlyBuyer
+ inState(State.Locked)
+ {
+ itemReceived();
+ buyer.send(value); // We ignore the return value on purpose
+ seller.send(this.balance);
+ state = State.Inactive;
+ }
+ function() { throw; }
+ }
+
+********************
+Micropayment Channel
+********************
+
+To be written.
diff --git a/docs/solidity-in-depth.rst b/docs/solidity-in-depth.rst
new file mode 100644
index 00000000..40704698
--- /dev/null
+++ b/docs/solidity-in-depth.rst
@@ -0,0 +1,19 @@
+#################
+Solidity in Depth
+#################
+
+This section should provide you with all you need to know about Solidity.
+If something is missing here, please contact us on
+`Gitter <https://gitter.im/ethereum/solidity>`_ or make a pull request on
+`Github <https://github.com/ethereum/solidity/pulls>`_.
+
+.. toctree::
+ :maxdepth: 2
+
+ layout-of-source-files.rst
+ structure-of-a-contract.rst
+ types.rst
+ units-and-global-variables.rst
+ control-structures.rst
+ contracts.rst
+ miscellaneous.rst
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
new file mode 100644
index 00000000..9910452f
--- /dev/null
+++ b/docs/structure-of-a-contract.rst
@@ -0,0 +1,128 @@
+.. index:: contract, state variable, function, event, struct, enum, function;modifier
+
+.. _contract_structure:
+
+***********************
+Structure of a Contract
+***********************
+
+Contracts in Solidity are similar to classes in object-oriented languages.
+Each contract can contain declarations of :ref:`structure-state-variables`, :ref:`structure-functions`,
+:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-structs-types` and :ref:`structure-enum-types`.
+Furthermore, contracts can inherit from other contracts.
+
+.. _structure-state-variables:
+
+State Variables
+===============
+
+State variables are values which are permanently stored in contract storage.
+
+::
+
+ contract SimpleStorage {
+ uint storedData; // State variable
+ // ...
+ }
+
+See the :ref:`types` section for valid state variable types and
+:ref:`visibility-and-accessors` for possible choices for
+visibility.
+
+.. _structure-functions:
+
+Functions
+=========
+
+Functions are the executable units of code within a contract.
+
+::
+
+ contract SimpleAuction {
+ function bid() { // Function
+ // ...
+ }
+ }
+
+:ref:`function-calls` can happen internally or externally
+and have different levels of visibility (:ref:`visibility-and-accessors`)
+towards other contracts.
+
+.. _structure-function-modifiers:
+
+Function Modifiers
+==================
+
+Function modifiers can be used to amend the semantics of functions in a declarative way
+(see :ref:`modifiers` in contracts section).
+
+::
+
+ contract Purchase {
+ address public seller;
+
+ modifier onlySeller() { // Modifier
+ if (msg.sender != seller) throw;
+ _
+ }
+
+ function abort() onlySeller { // Modifier usage
+ // ...
+ }
+ }
+
+ in the section on contracts for a more in-depth explanation.
+
+.. _structure-events:
+
+Events
+======
+
+Events are convenience interfaces with the EVM logging facilities.
+
+::
+
+ contract SimpleAuction {
+ event HighestBidIncreased(address bidder, uint amount); // Event
+
+ function bid() {
+ // ...
+ HighestBidIncreased(msg.sender, msg.value); // Triggering event
+ }
+ }
+
+See :ref:`events` in contracts section for information on how events are declared
+and can be used from within a dapp.
+
+.. _structure-structs-types:
+
+Structs Types
+=============
+
+Structs are custom defined types that can group several variables (see
+:ref:`structs` in types section).
+
+::
+
+ contract Ballot {
+ struct Voter { // Struct
+ uint weight;
+ bool voted;
+ address delegate;
+ uint vote;
+ }
+ }
+
+.. _structure-enum-types:
+
+Enum Types
+==========
+
+Enums can be used to create custom types with a finite set of values (see
+:ref:`enums` in types section).
+
+::
+
+ contract Purchase {
+ enum State { Created, Locked, Inactive } // Enum
+ }
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
new file mode 100644
index 00000000..0e782e23
--- /dev/null
+++ b/docs/style-guide.rst
@@ -0,0 +1,635 @@
+.. index:: style, coding style
+
+#############
+Style Guide
+#############
+
+************
+Introduction
+************
+
+This guide is intended to provide coding conventions for writing solidity code.
+This guide should be thought of as an evolving document that will change over
+time as useful conventions are found and old conventions are rendered obsolete.
+
+Many projects will implement their own style guides. In the event of
+conflicts, project specific style guides take precedence.
+
+The structure and many of the recommendations within this style guide were
+taken from python's
+`pep8 style guide <https://www.python.org/dev/peps/pep-0008/>`_.
+
+The goal of this guide is *not* to be the right way or the best way to write
+solidity code. The goal of this guide is *consistency*. A quote from python's
+`pep8 <https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds>`_
+captures this concept well.
+
+ A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.
+ But most importantly: know when to be inconsistent -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!
+
+
+***********
+Code Layout
+***********
+
+
+Indentation
+===========
+
+Use 4 spaces per indentation level.
+
+Tabs or Spaces
+==============
+
+Spaces are the preferred indentation method.
+
+Mixing tabs and spaces should be avoided.
+
+Blank Lines
+===========
+
+Surround top level declarations in solidity source with two blank lines.
+
+Yes::
+
+ contract A {
+ ...
+ }
+
+
+ contract B {
+ ...
+ }
+
+
+ contract C {
+ ...
+ }
+
+No::
+
+ contract A {
+ ...
+ }
+ contract B {
+ ...
+ }
+
+ contract C {
+ ...
+ }
+
+Within a contract surround function declarations with a single blank line.
+
+Blank lines may be omitted between groups of related one-liners (such as stub functions for an abstract contract)
+
+Yes::
+
+ contract A {
+ function spam();
+ function ham();
+ }
+
+
+ contract B is A {
+ function spam() {
+ ...
+ }
+
+ function ham() {
+ ...
+ }
+ }
+
+No::
+
+ contract A {
+ function spam() {
+ ...
+ }
+ function ham() {
+ ...
+ }
+ }
+
+Source File Encoding
+====================
+
+UTF-8 or ASCII encoding is preferred.
+
+Imports
+==========
+
+Import statements should always be placed at the top of the file.
+
+Yes::
+
+ import "owned";
+
+
+ contract A {
+ ...
+ }
+
+
+ contract B is owned {
+ ...
+ }
+
+No::
+
+ contract A {
+ ...
+ }
+
+
+ import "owned";
+
+
+ contract B is owned {
+ ...
+ }
+
+Whitespace in Expressions
+=========================
+
+Avoid extraneous whitespace in the following situations:
+
+* Immediately inside parenthesis, brackets or braces.
+
+Yes: `spam(ham[1], Coin({name: "ham"}));`
+
+No: `spam( ham[ 1 ], Coin( { name: "ham" } ) );`
+
+* Immediately before a comma, semicolon:
+
+Yes: `function spam(uint i, Coin coin);`
+
+No: `function spam(uint i , Coin coin) ;`
+
+* More than one space around an assignment or other operator to align with
+ another:
+
+Yes::
+
+ x = 1;
+ y = 2;
+ long_variable = 3;
+
+No::
+
+ x = 1;
+ y = 2;
+ long_variable = 3;
+
+
+Control Structures
+==================
+
+The braces denoting the body of a contract, library, functions and structs
+should:
+
+* open on the same line as the declaration
+* close on their own line at the same indentation level as the beginning of the
+ declaration.
+* The opening brace should be proceeded by a single space.
+
+Yes::
+
+ contract Coin {
+ struct Bank {
+ address owner;
+ uint balance;
+ }
+ }
+
+No::
+
+ contract Coin
+ {
+ struct Bank {
+ address owner;
+ uint balance;
+ }
+ }
+
+The same recommendations apply to the control structures `if`, `else`, `while`,
+and `for`.
+
+Additionally there should be a single space between the control structures
+`if`, `while`, and `for` and the parenthetic block representing the
+conditional, as well as a single space between the conditional parenthetic
+block and the opening brace.
+
+Yes::
+
+ if (...) {
+ ...
+ }
+
+ for (...) {
+ ...
+ }
+
+No::
+
+ if (...)
+ {
+ ...
+ }
+
+ while(...){
+ }
+
+ for (...) {
+ ...;}
+
+For control structures who's body contains a single statement, omitting the
+braces is ok *if* the statement is contained on a single line.
+
+Yes::
+
+ if (x < 10)
+ x += 1;
+
+No::
+
+ if (x < 10)
+ someArray.push(Coin({
+ name: 'spam',
+ value: 42
+ }));
+
+For `if` blocks which have an `else` or `else if` clause, the `else` should be
+placed on it's own line following the previous closing parenthesis. The
+parenthesis for the else block should follow the same rules as the other
+conditional control structures.
+
+Yes::
+
+ if (x < 3) {
+ x += 1;
+ }
+ else {
+ x -= 1;
+ }
+
+
+ if (x < 3)
+ x += 1;
+ else
+ x -= 1;
+
+No::
+
+ if (x < 3) {
+ x += 1;
+ } else {
+ x -= 1;
+ }
+
+Function Declaration
+====================
+
+For short function declarations, it is recommended for the opening brace of the
+function body to be kept on the same line as the function declaration.
+
+The closing brace should be at the same indentation level as the function
+declaration.
+
+The opening brace should be preceeded by a single space.
+
+Yes::
+
+ function increment(uint x) returns (uint) {
+ return x + 1;
+ }
+
+ function increment(uint x) public onlyowner returns (uint) {
+ return x + 1;
+ }
+
+No::
+
+ function increment(uint x) returns (uint)
+ {
+ return x + 1;
+ }
+
+ function increment(uint x) returns (uint){
+ return x + 1;
+ }
+
+ function increment(uint x) returns (uint) {
+ return x + 1;
+ }
+
+ function increment(uint x) returns (uint) {
+ return x + 1;}
+
+The visibility modifiers for a function should come before any custom
+modifiers.
+
+Yes::
+
+ function kill() public onlyowner {
+ selfdestruct(owner);
+ }
+
+No::
+
+ function kill() onlyowner public {
+ selfdestruct(owner);
+ }
+
+For long function declarations, it is recommended to drop each arguent onto
+it's own line at the same indentation level as the function body. The closing
+parenthesis and opening bracket should be placed on their own line as well at
+the same indentation level as the function declaration.
+
+Yes::
+
+ function thisFunctionHasLotsOfArguments(
+ address a,
+ address b,
+ address c,
+ address d,
+ address e,
+ address f,
+ ) {
+ do_something;
+ }
+
+No::
+
+ function thisFunctionHasLotsOfArguments(address a, address b, address c,
+ address d, address e, address f) {
+ do_something;
+ }
+
+ function thisFunctionHasLotsOfArguments(address a,
+ address b,
+ address c,
+ address d,
+ address e,
+ address f) {
+ do_something;
+ }
+
+ function thisFunctionHasLotsOfArguments(
+ address a,
+ address b,
+ address c,
+ address d,
+ address e,
+ address f) {
+ do_something;
+ }
+
+If a long function declaration has modifiers, then each modifier should be
+dropped to it's own line.
+
+Yes::
+
+ function thisFunctionNameIsReallyLong(address x, address y, address z)
+ public
+ onlyowner
+ priced
+ returns (address)
+ {
+ do_something;
+ }
+
+ function thisFunctionNameIsReallyLong(
+ address x,
+ address y,
+ address z,
+ )
+ public
+ onlyowner
+ priced
+ returns (address)
+ {
+ do_something;
+ }
+
+No::
+
+ function thisFunctionNameIsReallyLong(address x, address y, address z)
+ public
+ onlyowner
+ priced
+ returns (address) {
+ do_something;
+ }
+
+ function thisFunctionNameIsReallyLong(address x, address y, address z)
+ public onlyowner priced returns (address)
+ {
+ do_something;
+ }
+
+ function thisFunctionNameIsReallyLong(address x, address y, address z)
+ public
+ onlyowner
+ priced
+ returns (address) {
+ do_something;
+ }
+
+For constructor functions on inherited contracts who's bases require arguments,
+it is recommended to drop the base constructors onto new lines in the same
+manner as modifiers if the function declaration is long or hard to read.
+
+Yes::
+
+ contract A is B, C, D {
+ function A(uint param1, uint param2, uint param3, uint param4, uint param5)
+ B(param1)
+ C(param2, param3)
+ D(param4)
+ {
+ // do something with param5
+ }
+ }
+
+No::
+
+ contract A is B, C, D {
+ function A(uint param1, uint param2, uint param3, uint param4, uint param5)
+ B(param1)
+ C(param2, param3)
+ D(param4)
+ {
+ // do something with param5
+ }
+ }
+
+ contract A is B, C, D {
+ function A(uint param1, uint param2, uint param3, uint param4, uint param5)
+ B(param1)
+ C(param2, param3)
+ D(param4) {
+ // do something with param5
+ }
+ }
+
+
+These guidelines for function declarations are intended to improve readability.
+Authors should use their best judgement as this guide does not try to cover all
+possible permutations for function declarations.
+
+Mappings
+========
+
+TODO
+
+Variable Declarations
+=====================
+
+Declarations of array variables should not have a space between the type and
+the brackets.
+
+Yes: `uint[] x;`
+No: `uint [] x;`
+
+Other Recommendations
+=====================
+
+* Surround operators with a single space on either side.
+
+Yes::
+
+ x = 3;
+ x = 100 / 10;
+ x += 3 + 4;
+ x |= y && z;
+
+No::
+
+ x=3;
+ x = 100/10;
+ x += 3+4;
+ x |= y&&z;
+
+* Operators with a higher priority than others can exclude surrounding
+ whitespace in order to denote precedence. This is meant to allow for
+ improved readability for complex statement. You should always use the same
+ amount of whitespace on either side of an operator:
+
+Yes::
+
+ x = 2**3 + 5;
+ x = 2*y + 3*z;
+ x = (a+b) * (a-b);
+
+No::
+
+ x = 2** 3 + 5;
+ x = y+z;
+ x +=1;
+
+
+******************
+Naming Conventions
+******************
+
+Naming conventions are powerful when adopted and used broadly. The use of
+different conventions can convey significant *meta* information that would
+otherwise not be immediately available.
+
+The naming recommendations given here are intended to improve the readability,
+and thus they are not rules, but rather guidelines to try and help convey the
+most information through the names of things.
+
+Lastly, consistency within a codebase should always supercede any conventions
+outlined in this document.
+
+
+Naming Styles
+=============
+
+To avoid confusion, the following names will be used to refer to different
+naming styles.
+
+* ``b`` (single lowercase letter)
+* ``B`` (single uppercase letter)
+* ``lowercase``
+* ``lower_case_with_underscores``
+* ``UPPERCASE``
+* ``UPPER_CASE_WITH_UNDERSCORES``
+* ``CapitalizedWords`` (or CapWords)
+* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!)
+* ``Capitalized_Words_With_Underscores``
+
+.. note:: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError.
+
+
+Names to Avoid
+==============
+
+* ``l`` - Lowercase letter el
+* ``O`` - Uppercase letter oh
+* ``I`` - Uppercase letter eye
+
+Never use any of these for single letter variable names. They are often
+indistinguishable from the numerals one and zero.
+
+
+Contract and Library Names
+==========================
+
+Contracts should be named using the CapWords style.
+
+
+Events
+======
+
+Events should be named using the CapWords style.
+
+
+Function Names
+==============
+
+Functions should use mixedCase.
+
+
+Function Arguments
+==================
+
+When writing library functions that operate on a custom struct, the struct
+should be the first argument and should always be named ``self``.
+
+
+Local and State Variables
+=========================
+
+Use mixedCase.
+
+
+Constants
+=========
+
+Constants should be named with all capital letters with underscores separating
+words. (for example:``MAX_BLOCKS``)
+
+
+Modifiers
+=========
+
+Function modifiers should use lowercase words separated by underscores.
+
+
+Avoiding Collisions
+===================
+
+* ``single_trailing_underscore_``
+
+This convention is suggested when the desired name collides with that of a
+built-in or otherwise reserved name.
+
+
+General Recommendations
+=======================
+
+TODO
diff --git a/docs/types.rst b/docs/types.rst
new file mode 100644
index 00000000..187f62c1
--- /dev/null
+++ b/docs/types.rst
@@ -0,0 +1,571 @@
+.. index:: type
+
+.. _types:
+
+*****
+Types
+*****
+
+Solidity is a statically typed language, which means that the type of each
+variable (state and local) needs to be specified (or at least known -
+see :ref:`type-deduction` below) at
+compile-time. Solidity provides several elementary types which can be combined
+to complex types.
+
+.. index:: ! value type, ! type;value
+
+Value Types
+===========
+
+The following types are also called value types because variables of these
+types will always be passed by value, i.e. they are always copied when they
+are used as function arguments or in assignments.
+
+.. index:: ! bool, ! true, ! false
+
+Booleans
+--------
+
+`bool`: The possible values are constants `true` and `false`.
+
+Operators:
+
+* `!` (logical negation)
+* `&&` (logical conjunction, "and")
+* `||` (logical disjunction, "or")
+* `==` (equality)
+* `!=` (inequality)
+
+The operators `||` and `&&` apply the common short-circuiting rules. This means that in the expression `f(x) || g(y)`, if `f(x)` evaluates to `true`, `g(y)` will not be evaluated even if it may have side-effects.
+
+.. index:: ! uint, ! int, ! integer
+
+Integers
+--------
+
+`int•` / `uint•`: Signed and unsigned integers of various sizes. Keywords `uint8` to `uint256` in steps of `8` (unsigned of 8 up to 256 bits) and `int8` to `int256`. `uint` and `int` are aliases for `uint256` and `int256`, respectively.
+
+Operators:
+
+* Comparisons: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`)
+* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation)
+* Arithmetic operators: `+`, `-`, unary `-`, unary `+`, `*`, `/`, `%` (remainder), `**` (exponentiation)
+
+Division always truncates (it just maps to the DIV opcode of the EVM), but it does not truncate if both
+operators are :ref:`literals<integer_literals>` (or literal expressions).
+
+.. index:: address, balance, send, call, callcode, delegatecall
+
+Address
+-------
+
+`address`: Holds a 20 byte value (size of an Ethereum address). Address types also have members(see [Functions on addresses](#functions-on-addresses)) and serve as base for all contracts.
+
+Operators:
+
+* `<=`, `<`, `==`, `!=`, `>=` and `>`
+
+Members of Addresses
+^^^^^^^^^^^^^^^^^^^^
+
+* `balance` and `send`
+
+It is possible to query the balance of an address using the property `balance`
+and to send Ether (in units of wei) to an address using the `send` function:
+
+::
+
+ address x = 0x123;
+ address myAddress = this;
+ if (x.balance < 10 && myAddress.balance >= 10) x.send(10);
+
+.. note::
+ If `x` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the `send` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, `send` returns `false`.
+
+* `call`, `callcode` and `delegatecall`
+
+Furthermore, to interface with contracts that do not adhere to the ABI,
+the function `call` is provided which takes an arbitrary number of arguments of any type. These arguments are padded to 32 bytes and concatenated. One exception is the case where the first argument is encoded to exactly four bytes. In this case, it is not padded to allow the use of function signatures here.
+
+::
+
+ address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
+ nameReg.call("register", "MyName");
+ nameReg.call(bytes4(sha3("fun(uint256)")), a);
+
+`call` returns a boolean indicating whether the invoked function terminated (`true`) or caused an EVM exception (`false`). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance).
+
+In a similar way, the function `delegatecall` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of `delegatecall` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called `callcode` was available that did not provide access to the original `msg.sender` and `msg.value` values.
+
+All three functions `call`, `delegatecall` and `callcode` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
+
+.. note::
+ All contracts inherit the members of address, so it is possible to query the balance of the
+ current contract using `this.balance`.
+
+.. index:: byte array, bytes32
+
+
+Fixed-size byte arrays
+----------------------
+
+`bytes1`, `bytes2`, `bytes3`, ..., `bytes32`. `byte` is an alias for `bytes1`.
+
+Operators:
+
+* Comparisons: `<=`, `<`, `==`, `!=`, `>=`, `>` (evaluate to `bool`)
+* Bit operators: `&`, `|`, `^` (bitwise exclusive or), `~` (bitwise negation)
+* Index access: If `x` is of type `bytesI`, then `x[k]` for `0 <= k < I` returns the `k` th byte (read-only).
+
+Members:
+
+* `.length` yields the fixed length of the byte array (read-only).
+
+Dynamically-sized byte array
+----------------------------
+
+`bytes`:
+ Dynamically-sized byte array, see :ref:`arrays`. Not a value-type!
+`string`:
+ Dynamically-sized UTF8-encoded string, see :ref:`arrays`. Not a value-type!
+
+As a rule of thumb, use `bytes` for arbitrary-length raw byte data and `string`
+for arbitrary-length string (utf-8) data. If you can limit the length to a certain
+number of bytes, always use one of `bytes1` to `bytes32` because they are much cheaper.
+
+.. index:: literal, literal;integer
+
+.. _integer_literals:
+
+Integer Literals
+-----------------
+
+Integer Literals are arbitrary precision integers until they are used together with a non-literal. In `var x = 1 - 2;`, for example, the value of `1 - 2` is `-1`, which is assigned to `x` and thus `x` receives the type `int8` -- the smallest type that contains `-1`, although the natural types of `1` and `2` are actually `uint8`.
+
+It is even possible to temporarily exceed the maximum of 256 bits as long as only integer literals are used for the computation: `var x = (0xffffffffffffffffffff * 0xffffffffffffffffffff) * 0;` Here, `x` will have the value `0` and thus the type `uint8`.
+
+.. warning::
+ Divison on integer literals used to truncate in earlier versions, but it will actually convert into a rational number in the future, i.e. `1/2` is not equal to `0`, but to `0.5`.
+
+
+.. index:: literal, literal;string, string
+
+String Literals
+---------------
+
+String Literals are written with double quotes (`"abc"`). As with integer literals, their type can vary, but they are implicitly convertible to `bytes•` if they fit, to `bytes` and to `string`.
+
+.. index:: enum
+
+.. _enums:
+
+Enums
+=====
+
+Enums are one way to create a user-defined type in Solidity. They are explicitly convertible
+to and from all integer types but implicit conversion is not allowed.
+
+::
+
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
+ ActionChoices choice;
+ ActionChoices constant defaultChoice = ActionChoices.GoStraight;
+ function setGoStraight()
+ {
+ choice = ActionChoices.GoStraight;
+ }
+ // Since enum types are not part of the ABI, the signature of "getChoice"
+ // will automatically be changed to "getChoice() returns (uint8)"
+ // for all matters external to Solidity. The integer type used is just
+ // large enough to hold all enum values, i.e. if you have more values,
+ // `uint16` will be used and so on.
+ function getChoice() returns (ActionChoices)
+ {
+ return choice;
+ }
+ function getDefaultChoice() returns (uint)
+ {
+ return uint(defaultChoice);
+ }
+ }
+
+.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
+
+Reference Types
+==================
+
+Complex types, i.e. types which do not always fit into 256 bits have to be handled
+more carefully than the value-types we have already seen. Since copying
+them can be quite expensive, we have to think about whether we want them to be
+stored in **memory** (which is not persisting) or **storage** (where the state
+variables are held).
+
+Data location
+-------------
+
+Every complex type, i.e. *arrays* and *structs*, has an additional
+annotation, the "data location", about whether it is stored in memory or in storage. Depending on the
+context, there is always a default, but it can be overridden by appending
+either `storage` or `memory` to the type. The default for function parameters (including return parameters) is `memory`, the default for local variables is `storage` and the location is forced
+to `storage` for state variables (obviously).
+
+There is also a third data location, "calldata", which is a non-modifyable
+non-persistent area where function arguments are stored. Function parameters
+(not return parameters) of external functions are forced to "calldata" and
+it behaves mostly like memory.
+
+Data locations are important because they change how assignments behave:
+Assignments between storage and memory and also to a state variable (even from other state variables)
+always create an independent copy.
+Assignments to local storage variables only assign a reference though, and
+this reference always points to the state variable even if the latter is changed
+in the meantime.
+On the other hand, assignments from a memory stored reference type to another
+memory-stored reference type does not create a copy.
+
+::
+
+ contract c {
+ uint[] x; // the data location of x is storage
+ // the data location of memoryArray is memory
+ function f(uint[] memoryArray) {
+ x = memoryArray; // works, copies the whole array to storage
+ var y = x; // works, assigns a pointer, data location of y is storage
+ y[7]; // fine, returns the 8th element
+ y.length = 2; // fine, modifies x through y
+ delete x; // fine, clears the array, also modifies y
+ // The following does not work; it would need to create a new temporary /
+ // unnamed array in storage, but storage is "statically" allocated:
+ // y = memoryArray;
+ // This does not work either, since it would "reset" the pointer, but there
+ // is no sensible location it could point to.
+ // delete y;
+ g(x); // calls g, handing over a reference to x
+ h(x); // calls h and creates an independent, temporary copy in memory
+ }
+ function g(uint[] storage storageArray) internal {}
+ function h(uint[] memoryArray) {}
+ }
+
+Summary
+^^^^^^^
+
+Forced data location:
+ - parameters (not return) of external functions: calldata
+ - state variables: storage
+
+Default data location:
+ - parameters (also return) of functions: memory
+ - all other local variables: storage
+
+.. index:: ! array
+
+.. _arrays:
+
+Arrays
+------
+
+Arrays can have a compile-time fixed size or they can be dynamic.
+For storage arrays, the element type can be arbitrary (i.e. also other
+arrays, mappings or structs). For memory arrays, it cannot be a mapping and
+has to be an ABI type if it is an argument of a publicly-visible function.
+
+An array of fixed size `k` and element type `T` is written as `T[k]`,
+an array of dynamic size as `T[]`. As an example, an array of 5 dynamic
+arrays of `uint` is `uint[][5]` (note that the notation is reversed when
+compared to some other languages). To access the second uint in the
+third dynamic array, you use `x[2][1]` (indices are zero-based and
+access works in the opposite way of the declaration, i.e. `x[2]`
+shaves off one level in the type from the right).
+
+Variables of type `bytes` and `string` are special arrays. A `bytes` is similar to `byte[]`,
+but it is packed tightly in calldata. `string` is equal to `bytes` but does not allow
+length or index access (for now).
+
+So `bytes` should always be preferred over `byte[]` because it is cheaper.
+
+.. note::
+ If you want to access the byte-representation of a string `s`, use
+ `bytes(s).length` / `bytes(s)[7] = 'x';`. Keep in mind
+ that you are accessing the low-level bytes of the utf-8 representation,
+ and not the individual characters!
+
+.. index:: ! array;allocating, new
+
+Allocating Memory Arrays
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Creating arrays with variable length in memory can be done using the `new` keyword.
+As opposed to storage arrays, it is **not** possible to resize memory arrays by assigning to
+the `.length` member.
+
+::
+
+ contract C {
+ function f(uint len) {
+ uint[] memory a = new uint[](7);
+ bytes memory b = new bytes(len);
+ // Here we have a.length == 7 and b.length == len
+ a[6] = 8;
+ }
+ }
+
+
+.. index:: ! array;length, length, push, !array;push
+
+Members
+^^^^^^^
+
+**length**:
+ Arrays have a `length` member to hold their number of elements.
+ Dynamic arrays can be resized in storage (not in memory) by changing the
+ `.length` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created.
+**push**:
+ Dynamic storage arrays and `bytes` (not `string`) have a member function called `push` that can be used to append an element at the end of the array. The function returns the new length.
+
+.. warning::
+ It is not yet possible to use arrays of arrays in external functions.
+
+.. warning::
+ Due to limitations of the EVM, it is not possible to return
+ dynamic content from external function calls. The function `f` in
+ `contract C { function f() returns (uint[]) { ... } }` will return
+ something if called from web3.js, but not if called from Solidity.
+
+ The only workaround for now is to use large statically-sized arrays.
+
+
+::
+
+ contract ArrayContract {
+ uint[2**20] m_aLotOfIntegers;
+ // Note that the following is not a pair of arrays but an array of pairs.
+ bool[2][] m_pairsOfFlags;
+ // newPairs is stored in memory - the default for function arguments
+ function setAllFlagPairs(bool[2][] newPairs) {
+ // assignment to a storage array replaces the complete array
+ m_pairsOfFlags = newPairs;
+ }
+ function setFlagPair(uint index, bool flagA, bool flagB) {
+ // access to a non-existing index will throw an exception
+ m_pairsOfFlags[index][0] = flagA;
+ m_pairsOfFlags[index][1] = flagB;
+ }
+ function changeFlagArraySize(uint newSize) {
+ // if the new size is smaller, removed array elements will be cleared
+ m_pairsOfFlags.length = newSize;
+ }
+ function clear() {
+ // these clear the arrays completely
+ delete m_pairsOfFlags;
+ delete m_aLotOfIntegers;
+ // identical effect here
+ m_pairsOfFlags.length = 0;
+ }
+ bytes m_byteData;
+ function byteArrays(bytes data) {
+ // byte arrays ("bytes") are different as they are stored without padding,
+ // but can be treated identical to "uint8[]"
+ m_byteData = data;
+ m_byteData.length += 7;
+ m_byteData[3] = 8;
+ delete m_byteData[2];
+ }
+ function addFlag(bool[2] flag) returns (uint) {
+ return m_pairsOfFlags.push(flag);
+ }
+ function createMemoryArray(uint size) returns (bytes) {
+ // Dynamic memory arrays are created using `new`:
+ uint[2][] memory arrayOfPairs = new uint[2][](size);
+ // Create a dynamic byte array:
+ bytes memory b = new bytes(200);
+ for (uint i = 0; i < b.length; i++)
+ b[i] = byte(i);
+ return b;
+ }
+ }
+
+
+.. index:: ! struct, ! type;struct
+
+.. _structs:
+
+Structs
+-------
+
+Solidity provides a way to define new types in the form of structs, which is
+shown in the following example:
+
+::
+
+ contract CrowdFunding {
+ // Defines a new type with two fields.
+ struct Funder {
+ address addr;
+ uint amount;
+ }
+ struct Campaign {
+ address beneficiary;
+ uint fundingGoal;
+ uint numFunders;
+ uint amount;
+ mapping (uint => Funder) funders;
+ }
+ uint numCampaigns;
+ mapping (uint => Campaign) campaigns;
+ function newCampaign(address beneficiary, uint goal) returns (uint campaignID) {
+ campaignID = numCampaigns++; // campaignID is return variable
+ // Creates new struct and saves in storage. We leave out the mapping type.
+ campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
+ }
+ function contribute(uint campaignID) {
+ Campaign c = campaigns[campaignID];
+ // Creates a new temporary memory struct, initialised with the given values
+ // and copies it over to storage.
+ // Note that you can also use Funder(msg.sender, msg.value) to initialise.
+ c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
+ c.amount += msg.value;
+ }
+ function checkGoalReached(uint campaignID) returns (bool reached) {
+ Campaign c = campaigns[campaignID];
+ if (c.amount < c.fundingGoal)
+ return false;
+ c.beneficiary.send(c.amount);
+ c.amount = 0;
+ return true;
+ }
+ }
+
+The contract does not provide the full functionality of a crowdfunding
+contract, but it contains the basic concepts necessary to understand structs.
+Struct types can be used inside mappings and arrays and they can itself
+contain mappings and arrays.
+
+It is not possible for a struct to contain a member of its own type,
+although the struct itself can be the value type of a mapping member.
+This restriction is necessary, as the size of the struct has to be finite.
+
+Note how in all the functions, a struct type is assigned to a local variable
+(of the default storage data location).
+This does not copy the struct but only stores a reference so that assignments to
+members of the local variable actually write to the state.
+
+Of course, you can also directly access the members of the struct without
+assigning it to a local variable, as in
+`campaigns[campaignID].amount = 0`.
+
+.. index:: !mapping
+
+Mappings
+========
+
+Mapping types are declared as `mapping _KeyType => _ValueType`, where
+`_KeyType` can be almost any type except for a mapping and `_ValueType`
+can actually be any type, including mappings.
+
+Mappings can be seen as hashtables which are virtually initialized such that
+every possible key exists and is mapped to a value whose byte-representation is
+all zeros. The similarity ends here, though: The key data is not actually stored
+in a mapping, only its `sha3` hash used to look up the value.
+
+Because of this, mappings do not have a length or a concept of a key or value being "set".
+
+Mappings are only allowed for state variables (or as storage reference types
+in internal functions).
+
+.. index:: assignment, ! delete, lvalue
+
+Operators Involving LValues
+===========================
+
+If `a` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands:
+
+`a += e` is equivalent to `a = a + e`. The operators `-=`, `*=`, `/=`, `%=`, `a |=`, `&=` and `^=` are defined accordingly. `a++` and `a--` are equivalent to `a += 1` / `a -= 1` but the expression itself still has the previous value of `a`. In contrast, `--a` and `++a` have the same effect on `a` but return the value after the change.
+
+delete
+------
+
+`delete a` assigns the initial value for the type to `a`. I.e. for integers it is equivalent to `a = 0`, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset.
+
+`delete` has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted.
+
+It is important to note that `delete a` really behaves like an assignment to `a`, i.e. it stores a new object in `a`.
+
+::
+
+ contract DeleteExample {
+ uint data;
+ uint[] dataArray;
+ function f() {
+ uint x = data;
+ delete x; // sets x to 0, does not affect data
+ delete data; // sets data to 0, does not affect x which still holds a copy
+ uint[] y = dataArray;
+ delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
+ // y is affected which is an alias to the storage object
+ // On the other hand: "delete y" is not valid, as assignments to local variables
+ // referencing storage objects can only be made from existing storage objects.
+ }
+ }
+
+.. index:: ! type;conversion, ! cast
+
+Conversions between Elementary Types
+====================================
+
+Implicit Conversions
+--------------------
+
+If an operator is applied to different types, the compiler tries to
+implicitly convert one of the operands to the type of the other (the same is
+true for assignments). In general, an implicit conversion between value-types
+is possible if it
+makes sense semantically and no information is lost: `uint8` is convertible to
+`uint16` and `int128` to `int256`, but `int8` is not convertible to `uint256`
+(because `uint256` cannot hold e.g. `-1`).
+Furthermore, unsigned integers can be converted to bytes of the same or larger
+size, but not vice-versa. Any type that can be converted to `uint160` can also
+be converted to `address`.
+
+Explicit Conversions
+--------------------
+
+If the compiler does not allow implicit conversion but you know what you are
+doing, an explicit type conversion is sometimes possible::
+
+ int8 y = -3;
+ uint x = uint(y);
+
+At the end of this code snippet, `x` will have the value `0xfffff..fd` (64 hex
+characters), which is -3 in two's complement representation of 256 bits.
+
+If a type is explicitly converted to a smaller type, higher-order bits are
+cut off::
+
+ uint32 a = 0x12345678;
+ uint16 b = uint16(a); // b will be 0x5678 now
+
+.. index:: ! type;deduction, ! var
+
+.. _type-deduction:
+
+Type Deduction
+==============
+
+For convenience, it is not always necessary to explicitly specify the type of a
+variable, the compiler automatically infers it from the type of the first
+expression that is assigned to the variable::
+
+ uint20 x = 0x123;
+ var y = x;
+
+Here, the type of `y` will be `uint20`. Using `var` is not possible for function
+parameters or return parameters.
+
+.. warning::
+ The type is only deduced from the first assignment, so
+ the loop in the following snippet is infinite, as `i` will have the type
+ `uint8` and any value of this type is smaller than `2000`.
+ `for (var i = 0; i < 2000; i++) { ... }`
+
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
new file mode 100644
index 00000000..a4239a29
--- /dev/null
+++ b/docs/units-and-global-variables.rst
@@ -0,0 +1,125 @@
+**************************************
+Units and Globally Available Variables
+**************************************
+
+.. index:: wei, finney, szabo, ether
+
+Ether Units
+===========
+
+A literal number can take a suffix of `wei`, `finney`, `szabo` or `ether` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be "wei", e.g. `2 ether == 2000 finney` evaluates to `true`.
+
+.. index:: time, seconds, minutes, hours, days, weeks, years
+
+Time Units
+==========
+
+Suffixes of `seconds`, `minutes`, `hours`, `days`, `weeks` and
+`years` after literal numbers can be used to convert between units of time where seconds are the base
+unit and units are considered naively in the following way:
+
+ * `1 == 1 second`
+ * `1 minutes == 60 seconds`
+ * `1 hours == 60 minutes`
+ * `1 days == 24 hours`
+ * `1 weeks = 7 days`
+ * `1 years = 365 days`
+
+Take care if you perform calendar calculations using these units, because
+not every year equals 365 days and not even every day has 24 hours
+because of `leap seconds <https://en.wikipedia.org/wiki/Leap_second>`_.
+Due to the fact that leap seconds cannot be predicted, an exact calendar
+library has to be updated by an external oracle.
+
+These suffixes cannot be applied to variables. If you want to
+interpret some input variable in e.g. days, you can do it in the following way::
+
+ function f(uint start, uint daysAfter) {
+ if (now >= start + daysAfter * 1 days) { ... }
+ }
+
+Special Variables and Functions
+===============================
+
+There are special variables and functions which always exist in the global
+namespace and are mainly used to provide information about the blockchain.
+
+.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin
+
+
+Block and Transaction Properties
+------------------------------------
+
+ - `block.coinbase` (`address`): current block miner's address
+ - `block.difficulty` (`uint`): current block difficulty
+ - `block.gaslimit` (`uint`): current block gaslimit
+ - `block.number` (`uint`): current block number
+ - `block.blockhash` (`function(uint) returns (bytes32)`): hash of the given block - only for 256 most recent blocks
+ - `block.timestamp` (`uint`): current block timestamp
+ - `msg.data` (`bytes`): complete calldata
+ - `msg.gas` (`uint`): remaining gas
+ - `msg.sender` (`address`): sender of the message (current call)
+ - `msg.sig` (`bytes4`): first four bytes of the calldata (i.e. function identifier)
+ - `msg.value` (`uint`): number of wei sent with the message
+ - `now` (`uint`): current block timestamp (alias for `block.timestamp`)
+ - `tx.gasprice` (`uint`): gas price of the transaction
+ - `tx.origin` (`address`): sender of the transaction (full call chain)
+
+.. note::
+ The values of all members of `msg`, including `msg.sender` and
+ `msg.value` can change for every **external** function call.
+ This includes calls to library functions.
+
+ If you want to implement access restrictions in library functions using
+ `msg.sender`, you have to manually supply the value of
+ `msg.sender` as an argument.
+
+.. note::
+ The block hashes are not available for all blocks for scalability reasons.
+ You can only access the hashes of the most recent 256 blocks, all other
+ values will be zero.
+
+.. index:: sha3, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
+
+Mathematical and Cryptographic Functions
+----------------------------------------
+
+`addmod(uint x, uint y, uint k) returns (uint)`:
+ compute `(x + y) % k` where the addition is performed with arbitrary precision and does not wrap around at `2**256`.
+`mulmod(uint x, uint y, uint k) returns (uint)`:
+ compute `(x * y) % k` where the multiplication is performed with arbitrary precision and does not wrap around at `2**256`.
+`sha3(...) returns (bytes32)`:
+ compute the Ethereum-SHA-3 hash of the (tightly packed) arguments
+`sha256(...) returns (bytes32)`:
+ compute the SHA-256 hash of the (tightly packed) arguments
+`ripemd160(...) returns (bytes20)`:
+ compute RIPEMD-160 hash of the (tightly packed) arguments
+`ecrecover(bytes32, uint8, bytes32, bytes32) returns (address)`:
+ recover public key from elliptic curve signature - arguments are (data, v, r, s)
+
+In the above, "tightly packed" means that the arguments are concatenated without padding.
+This means that the following are all identical::
+
+ sha3("ab", "c")
+ sha3("abc")
+ sha3(0x616263)
+ sha3(6382179)
+ sha3(97, 98, 99)
+
+If padding is needed, explicit type conversions can be used: `sha3("\x00\x12")` is the
+same as `sha3(uint16(0x12))`.
+
+It might be that you run into Out-of-Gas for `sha256`, `ripemd160` or `ecrecover` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
+
+.. index:: this, selfdestruct
+
+Contract Related
+----------------
+
+`this` (current contract's type):
+ the current contract, explicitly convertible to `address`
+`selfdestruct(address)`:
+ destroy the current contract, sending its funds to the given address
+
+Furthermore, all functions of the current contract are callable directly including the current function.
+
diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py
new file mode 100644
index 00000000..d05c7b0c
--- /dev/null
+++ b/docs/utils/SolidityLexer.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+import re
+import copy
+
+from pygments.lexer import RegexLexer, ExtendedRegexLexer, bygroups, using, \
+ include, this
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+ Number, Other, Punctuation, Literal
+
+__all__ = ['SolidityLexer']
+
+class SolidityLexer(RegexLexer):
+ name = "Solidity"
+ aliases = ['sol', 'solidity']
+ filenames = ['*.sol']
+ mimetypes = []
+ flags = re.DOTALL
+ tokens = {
+ 'commentsandwhitespace': [
+ (r'\s+', Text),
+ (r'<!--', Comment),
+ (r'///', Comment.Special, 'docstringsingle'),
+ (r'//.*?\n', Comment.Single),
+ (r'/\*\*', Comment.Special, 'docstringmulti'),
+ (r'/\*.*?\*/', Comment.Multiline)
+ ],
+ 'natspec': [
+ (r'@author|@dev|@notice|@return|@param|@why3|@title', Keyword),
+ (r'.[^@*\n]*?', Comment.Special)
+ ],
+ 'docstringsingle': [
+ (r'\n', Comment.Special, '#pop'),
+ include('natspec')
+ ],
+ 'docstringmulti': [
+ (r'\*/', Comment.Special, '#pop'),
+ include('natspec')
+ ],
+ 'slashstartsregex': [
+ include('commentsandwhitespace'),
+ (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
+ r'([gim]+\b|\B)', String.Regex, '#pop'),
+ (r'(?=/)', Text, ('#pop', 'badregex')),
+ (r'', Text, '#pop')
+ ],
+ 'badregex': [
+ (r'\n', Text, '#pop')
+ ],
+ 'root': [
+ (r'^(?=\s|/|<!--)', Text, 'slashstartsregex'),
+ include('commentsandwhitespace'),
+ (r'\+\+|--|\*\*|~|&&|\?|:|\|\||\\(?=\n)|'
+ r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'),
+ (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
+ (r'[})\].]', Punctuation),
+ (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|'
+ r'throw|try|catch|finally|new|delete|typeof|instanceof|void|'
+ r'this|import|mapping|returns|private|public|external|internal|'
+ r'constant|memory|storage)\b', Keyword, 'slashstartsregex'),
+ (r'(var|let|with|function|event|modifier|struct|enum|contract)\b', Keyword.Declaration, 'slashstartsregex'),
+ (r'(bytes|string|address|uint|int|bool|byte|' +
+ '|'.join(
+ ['uint%d' % (i + 8) for i in range(0, 256, 8)] +
+ ['int%d' % (i + 8) for i in range(0, 256, 8)] +
+ ['bytes%d' % (i + 1) for i in range(0, 32)]
+ ) + r')\b', Keyword.Type, 'slashstartsregex'),
+ (r'(abstract|boolean|byte|char|class|const|debugger|double|enum|export|'
+ r'extends|final|float|goto|implements|int|interface|long|native|'
+ r'package|private|protected|public|short|static|super|synchronized|throws|'
+ r'transient|volatile)\b', Keyword.Reserved),
+ (r'(true|false|null|NaN|Infinity|undefined)\b', Keyword.Constant),
+ (r'(Array|Boolean|Date|Error|Function|Math|netscape|'
+ r'Number|Object|Packages|RegExp|String|sun|decodeURI|'
+ r'decodeURIComponent|encodeURI|encodeURIComponent|'
+ r'Error|eval|isFinite|isNaN|parseFloat|parseInt|document|this|'
+ r'window)\b', Name.Builtin),
+ (r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other),
+ (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
+ (r'0x[0-9a-fA-F]+', Number.Hex),
+ (r'[0-9]+', Number.Integer),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ ]
+ }
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
new file mode 100644
index 00000000..fa2f4e9c
--- /dev/null
+++ b/libsolidity/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
+
+aux_source_directory(analysis SRC_LIST)
+aux_source_directory(ast SRC_LIST)
+aux_source_directory(codegen SRC_LIST)
+aux_source_directory(formal SRC_LIST)
+aux_source_directory(interface SRC_LIST)
+aux_source_directory(parsing SRC_LIST)
+
+set(EXECUTABLE solidity)
+
+file(GLOB HEADERS "*/*.h")
+
+include_directories(BEFORE ..)
+add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
+
+eth_use(${EXECUTABLE} REQUIRED Dev::devcore Eth::evmcore Eth::evmasm)
+
+install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
+install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
+
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
new file mode 100644
index 00000000..6beb655e
--- /dev/null
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -0,0 +1,59 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Evaluator for types of constant expressions.
+ */
+
+#include <libsolidity/analysis/ConstantEvaluator.h>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
+{
+ TypePointer const& subType = _operation.subExpression().annotation().type;
+ if (!dynamic_cast<IntegerConstantType const*>(subType.get()))
+ BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression."));
+ TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
+ _operation.annotation().type = t;
+}
+
+void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
+{
+ TypePointer const& leftType = _operation.leftExpression().annotation().type;
+ TypePointer const& rightType = _operation.rightExpression().annotation().type;
+ if (!dynamic_cast<IntegerConstantType const*>(leftType.get()))
+ BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression."));
+ if (!dynamic_cast<IntegerConstantType const*>(rightType.get()))
+ BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression."));
+ TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ if (Token::isCompareOp(_operation.getOperator()))
+ commonType = make_shared<BoolType>();
+ _operation.annotation().type = commonType;
+}
+
+void ConstantEvaluator::endVisit(Literal const& _literal)
+{
+ _literal.annotation().type = Type::forLiteral(_literal);
+ if (!_literal.annotation().type)
+ BOOST_THROW_EXCEPTION(_literal.createTypeError("Invalid literal value."));
+}
diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h
new file mode 100644
index 00000000..f311efbf
--- /dev/null
+++ b/libsolidity/analysis/ConstantEvaluator.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Evaluator for types of constant expressions.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class TypeChecker;
+
+/**
+ * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants.
+ */
+class ConstantEvaluator: private ASTConstVisitor
+{
+public:
+ ConstantEvaluator(Expression const& _expr) { _expr.accept(*this); }
+
+private:
+ virtual void endVisit(BinaryOperation const& _operation);
+ virtual void endVisit(UnaryOperation const& _operation);
+ virtual void endVisit(Literal const& _literal);
+
+};
+
+}
+}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
new file mode 100644
index 00000000..042b7a6a
--- /dev/null
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -0,0 +1,96 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Scope - object that holds declaration of names.
+ */
+
+#include <libsolidity/analysis/DeclarationContainer.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+Declaration const* DeclarationContainer::conflictingDeclaration(
+ Declaration const& _declaration,
+ ASTString const* _name
+) const
+{
+ if (!_name)
+ _name = &_declaration.name();
+ solAssert(!_name->empty(), "");
+ vector<Declaration const*> declarations;
+ if (m_declarations.count(*_name))
+ declarations += m_declarations.at(*_name);
+ if (m_invisibleDeclarations.count(*_name))
+ declarations += m_invisibleDeclarations.at(*_name);
+
+ if (dynamic_cast<FunctionDefinition const*>(&_declaration))
+ {
+ // check that all other declarations with the same name are functions
+ for (Declaration const* declaration: declarations)
+ if (!dynamic_cast<FunctionDefinition const*>(declaration))
+ return declaration;
+ }
+ else if (declarations.size() == 1 && declarations.front() == &_declaration)
+ return nullptr;
+ else if (!declarations.empty())
+ return declarations.front();
+
+ return nullptr;
+}
+
+bool DeclarationContainer::registerDeclaration(
+ Declaration const& _declaration,
+ ASTString const* _name,
+ bool _invisible,
+ bool _update
+)
+{
+ if (!_name)
+ _name = &_declaration.name();
+ if (_name->empty())
+ return true;
+
+ if (_update)
+ {
+ solAssert(!dynamic_cast<FunctionDefinition const*>(&_declaration), "Attempt to update function definition.");
+ m_declarations.erase(*_name);
+ m_invisibleDeclarations.erase(*_name);
+ }
+ else if (conflictingDeclaration(_declaration, _name))
+ return false;
+
+ vector<Declaration const*>& decls = _invisible ? m_invisibleDeclarations[*_name] : m_declarations[*_name];
+ if (!contains(decls, &_declaration))
+ decls.push_back(&_declaration);
+ return true;
+}
+
+std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
+{
+ solAssert(!_name.empty(), "Attempt to resolve empty name.");
+ auto result = m_declarations.find(_name);
+ if (result != m_declarations.end())
+ return result->second;
+ if (_recursive && m_enclosingContainer)
+ return m_enclosingContainer->resolveName(_name, true);
+ return vector<Declaration const*>({});
+}
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
new file mode 100644
index 00000000..5862f7a5
--- /dev/null
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -0,0 +1,68 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Scope - object that holds declaration of names.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <boost/noncopyable.hpp>
+
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Container that stores mappings between names and declarations. It also contains a link to the
+ * enclosing scope.
+ */
+class DeclarationContainer
+{
+public:
+ explicit DeclarationContainer(
+ ASTNode const* _enclosingNode = nullptr,
+ DeclarationContainer const* _enclosingContainer = nullptr
+ ):
+ m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) {}
+ /// Registers the declaration in the scope unless its name is already declared or the name is empty.
+ /// @param _name the name to register, if nullptr the intrinsic name of @a _declaration is used.
+ /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
+ /// @param _update if true, replaces a potential declaration that is already present
+ /// @returns false if the name was already declared.
+ bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
+ std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
+ ASTNode const* enclosingNode() const { return m_enclosingNode; }
+ std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
+ /// @returns whether declaration is valid, and if not also returns previous declaration.
+ Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
+
+private:
+ ASTNode const* m_enclosingNode;
+ DeclarationContainer const* m_enclosingContainer;
+ std::map<ASTString, std::vector<Declaration const*>> m_declarations;
+ std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;
+};
+
+}
+}
diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp
new file mode 100644
index 00000000..4f75f03d
--- /dev/null
+++ b/libsolidity/analysis/DocStringAnalyser.cpp
@@ -0,0 +1,128 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Parses and analyses the doc strings.
+ * Stores the parsing results in the AST annotations and reports errors.
+ */
+
+#include <libsolidity/analysis/DocStringAnalyser.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/parsing/DocStringParser.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
+{
+ m_errorOccured = false;
+ _sourceUnit.accept(*this);
+
+ return !m_errorOccured;
+}
+
+bool DocStringAnalyser::visit(ContractDefinition const& _node)
+{
+ static const set<string> validTags = set<string>{"author", "title", "dev", "notice", "why3"};
+ parseDocStrings(_node, _node.annotation(), validTags, "contracts");
+
+ return true;
+}
+
+bool DocStringAnalyser::visit(FunctionDefinition const& _node)
+{
+ handleCallable(_node, _node, _node.annotation());
+ return true;
+}
+
+bool DocStringAnalyser::visit(ModifierDefinition const& _node)
+{
+ handleCallable(_node, _node, _node.annotation());
+
+ return true;
+}
+
+bool DocStringAnalyser::visit(EventDefinition const& _node)
+{
+ handleCallable(_node, _node, _node.annotation());
+
+ return true;
+}
+
+bool DocStringAnalyser::visitNode(ASTNode const& _node)
+{
+ if (auto node = dynamic_cast<Statement const*>(&_node))
+ {
+ static const set<string> validTags = {"why3"};
+ parseDocStrings(*node, node->annotation(), validTags, "statements");
+ }
+ return true;
+}
+
+void DocStringAnalyser::handleCallable(
+ CallableDeclaration const& _callable,
+ Documented const& _node,
+ DocumentedAnnotation& _annotation
+)
+{
+ static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param", "why3"};
+ parseDocStrings(_node, _annotation, validTags, "functions");
+
+ set<string> validParams;
+ for (auto const& p: _callable.parameters())
+ validParams.insert(p->name());
+ if (_callable.returnParameterList())
+ for (auto const& p: _callable.returnParameterList()->parameters())
+ validParams.insert(p->name());
+ auto paramRange = _annotation.docTags.equal_range("param");
+ for (auto i = paramRange.first; i != paramRange.second; ++i)
+ if (!validParams.count(i->second.paramName))
+ appendError(
+ "Documented parameter \"" +
+ i->second.paramName +
+ "\" not found in the parameter list of the function."
+ );
+}
+
+void DocStringAnalyser::parseDocStrings(
+ Documented const& _node,
+ DocumentedAnnotation& _annotation,
+ set<string> const& _validTags,
+ string const& _nodeName
+)
+{
+ DocStringParser parser;
+ if (_node.documentation() && !_node.documentation()->empty())
+ {
+ if (!parser.parse(*_node.documentation(), m_errors))
+ m_errorOccured = true;
+ _annotation.docTags = parser.tags();
+ }
+ for (auto const& docTag: _annotation.docTags)
+ if (!_validTags.count(docTag.first))
+ appendError("Doc tag @" + docTag.first + " not valid for " + _nodeName + ".");
+}
+
+void DocStringAnalyser::appendError(string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DocstringParsingError);
+ *err << errinfo_comment(_description);
+ m_errors.push_back(err);
+ m_errorOccured = true;
+}
diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h
new file mode 100644
index 00000000..cdf297e3
--- /dev/null
+++ b/libsolidity/analysis/DocStringAnalyser.h
@@ -0,0 +1,71 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Parses and analyses the doc strings.
+ * Stores the parsing results in the AST annotations and reports errors.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Parses and analyses the doc strings.
+ * Stores the parsing results in the AST annotations and reports errors.
+ */
+class DocStringAnalyser: private ASTConstVisitor
+{
+public:
+ DocStringAnalyser(ErrorList& _errors): m_errors(_errors) {}
+ bool analyseDocStrings(SourceUnit const& _sourceUnit);
+
+private:
+ virtual bool visit(ContractDefinition const& _contract) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+ virtual bool visit(ModifierDefinition const& _modifier) override;
+ virtual bool visit(EventDefinition const& _event) override;
+
+ virtual bool visitNode(ASTNode const&) override;
+
+ void handleCallable(
+ CallableDeclaration const& _callable,
+ Documented const& _node,
+ DocumentedAnnotation& _annotation
+ );
+
+ void parseDocStrings(
+ Documented const& _node,
+ DocumentedAnnotation& _annotation,
+ std::set<std::string> const& _validTags,
+ std::string const& _nodeName
+ );
+
+ void appendError(std::string const& _description);
+
+ bool m_errorOccured = false;
+ ErrorList& m_errors;
+};
+
+}
+}
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
new file mode 100644
index 00000000..a7ffcfad
--- /dev/null
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -0,0 +1,102 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#include <memory>
+#include <libsolidity/analysis/GlobalContext.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+GlobalContext::GlobalContext():
+m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
+ make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
+ make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
+ make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
+ make_shared<MagicVariableDeclaration>("suicide",
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
+ make_shared<MagicVariableDeclaration>("selfdestruct",
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
+ make_shared<MagicVariableDeclaration>("addmod",
+ make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::AddMod)),
+ make_shared<MagicVariableDeclaration>("mulmod",
+ make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::MulMod)),
+ make_shared<MagicVariableDeclaration>("sha3",
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
+ make_shared<MagicVariableDeclaration>("log0",
+ make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
+ make_shared<MagicVariableDeclaration>("log1",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)),
+ make_shared<MagicVariableDeclaration>("log2",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)),
+ make_shared<MagicVariableDeclaration>("log3",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)),
+ make_shared<MagicVariableDeclaration>("log4",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)),
+ make_shared<MagicVariableDeclaration>("sha256",
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)),
+ make_shared<MagicVariableDeclaration>("ecrecover",
+ make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
+ make_shared<MagicVariableDeclaration>("ripemd160",
+ make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))})
+{
+}
+
+void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
+{
+ m_currentContract = &_contract;
+}
+
+vector<Declaration const*> GlobalContext::declarations() const
+{
+ vector<Declaration const*> declarations;
+ declarations.reserve(m_magicVariables.size());
+ for (ASTPointer<Declaration const> const& variable: m_magicVariables)
+ declarations.push_back(variable.get());
+ return declarations;
+}
+
+MagicVariableDeclaration const* GlobalContext::currentThis() const
+{
+ if (!m_thisPointer[m_currentContract])
+ m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ "this", make_shared<ContractType>(*m_currentContract));
+ return m_thisPointer[m_currentContract].get();
+
+}
+
+MagicVariableDeclaration const* GlobalContext::currentSuper() const
+{
+ if (!m_superPointer[m_currentContract])
+ m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ "super", make_shared<ContractType>(*m_currentContract, true));
+ return m_superPointer[m_currentContract].get();
+}
+
+}
+}
diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h
new file mode 100644
index 00000000..482391d3
--- /dev/null
+++ b/libsolidity/analysis/GlobalContext.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+#include <memory>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Type; // forward
+
+/**
+ * Container for all global objects which look like AST nodes, but are not part of the AST
+ * that is currently being compiled.
+ * @note must not be destroyed or moved during compilation as its objects can be referenced from
+ * other objects.
+ */
+class GlobalContext: private boost::noncopyable
+{
+public:
+ GlobalContext();
+ void setCurrentContract(ContractDefinition const& _contract);
+ MagicVariableDeclaration const* currentThis() const;
+ MagicVariableDeclaration const* currentSuper() const;
+
+ /// @returns a vector of all implicit global declarations excluding "this".
+ std::vector<Declaration const*> declarations() const;
+
+private:
+ std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
+ ContractDefinition const* m_currentContract = nullptr;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
+};
+
+}
+}
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
new file mode 100644
index 00000000..5e407383
--- /dev/null
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -0,0 +1,642 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Parser part that determines the declarations corresponding to names and the types of expressions.
+ */
+
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/interface/Exceptions.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+NameAndTypeResolver::NameAndTypeResolver(
+ vector<Declaration const*> const& _globals,
+ ErrorList& _errors
+) :
+ m_errors(_errors)
+{
+ if (!m_scopes[nullptr])
+ m_scopes[nullptr].reset(new DeclarationContainer());
+ for (Declaration const* declaration: _globals)
+ m_scopes[nullptr]->registerDeclaration(*declaration);
+}
+
+bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
+{
+ if (!m_scopes[&_sourceUnit])
+ // By importing, it is possible that the container already exists.
+ m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
+ m_currentScope = m_scopes[&_sourceUnit].get();
+
+ // The helper registers all declarations in m_scopes as a side-effect of its construction.
+ try
+ {
+ DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
+ _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
+ }
+ catch (FatalError const&)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, SourceUnit const*> const& _sourceUnits)
+{
+ DeclarationContainer& target = *m_scopes.at(&_sourceUnit);
+ bool error = false;
+ for (auto const& node: _sourceUnit.nodes())
+ if (auto imp = dynamic_cast<ImportDirective const*>(node.get()))
+ {
+ string const& path = imp->annotation().absolutePath;
+ if (!_sourceUnits.count(path))
+ {
+ reportDeclarationError(
+ imp->location(),
+ "Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
+ );
+ error = true;
+ continue;
+ }
+ auto scope = m_scopes.find(_sourceUnits.at(path));
+ solAssert(scope != end(m_scopes), "");
+ if (!imp->symbolAliases().empty())
+ for (auto const& alias: imp->symbolAliases())
+ {
+ auto declarations = scope->second->resolveName(alias.first->name(), false);
+ if (declarations.empty())
+ {
+ reportDeclarationError(
+ imp->location(),
+ "Declaration \"" +
+ alias.first->name() +
+ "\" not found in \"" +
+ path +
+ "\" (referenced as \"" +
+ imp->path() +
+ "\")."
+ );
+ error = true;
+ }
+ else
+ for (Declaration const* declaration: declarations)
+ {
+ ASTString const* name = alias.second ? alias.second.get() : &declaration->name();
+ if (!target.registerDeclaration(*declaration, name))
+ {
+ reportDeclarationError(
+ imp->location(),
+ "Identifier \"" + *name + "\" already declared."
+ );
+ error = true;
+ }
+ }
+ }
+ else if (imp->name().empty())
+ for (auto const& nameAndDeclaration: scope->second->declarations())
+ for (auto const& declaration: nameAndDeclaration.second)
+ if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first))
+ {
+ reportDeclarationError(
+ imp->location(),
+ "Identifier \"" + nameAndDeclaration.first + "\" already declared."
+ );
+ error = true;
+ }
+ }
+ return !error;
+}
+
+bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
+{
+ try
+ {
+ m_currentScope = m_scopes[_contract.scope()].get();
+ solAssert(!!m_currentScope, "");
+
+ ReferencesResolver resolver(m_errors, *this, nullptr);
+ bool success = true;
+ for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts())
+ if (!resolver.resolve(*baseContract))
+ success = false;
+
+ m_currentScope = m_scopes[&_contract].get();
+
+ if (success)
+ {
+ linearizeBaseContracts(_contract);
+ vector<ContractDefinition const*> properBases(
+ ++_contract.annotation().linearizedBaseContracts.begin(),
+ _contract.annotation().linearizedBaseContracts.end()
+ );
+
+ for (ContractDefinition const* base: properBases)
+ importInheritedScope(*base);
+ }
+
+ // these can contain code, only resolve parameters for now
+ for (ASTPointer<ASTNode> const& node: _contract.subNodes())
+ {
+ m_currentScope = m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract].get();
+ if (!resolver.resolve(*node))
+ success = false;
+ }
+
+ if (!success)
+ return false;
+
+ m_currentScope = m_scopes[&_contract].get();
+
+ // now resolve references inside the code
+ for (ModifierDefinition const* modifier: _contract.functionModifiers())
+ {
+ m_currentScope = m_scopes[modifier].get();
+ ReferencesResolver resolver(m_errors, *this, nullptr, true);
+ if (!resolver.resolve(*modifier))
+ success = false;
+ }
+
+ for (FunctionDefinition const* function: _contract.definedFunctions())
+ {
+ m_currentScope = m_scopes[function].get();
+ if (!ReferencesResolver(
+ m_errors,
+ *this,
+ function->returnParameterList().get(),
+ true
+ ).resolve(*function))
+ success = false;
+ }
+ if (!success)
+ return false;
+ }
+ catch (FatalError const&)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
+{
+ try
+ {
+ m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true);
+ solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
+ }
+ catch (FatalError const&)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
+{
+ auto iterator = m_scopes.find(_scope);
+ if (iterator == end(m_scopes))
+ return vector<Declaration const*>({});
+ return iterator->second->resolveName(_name, false);
+}
+
+vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
+{
+ return m_currentScope->resolveName(_name, _recursive);
+}
+
+Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
+{
+ solAssert(!_path.empty(), "");
+ vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
+ for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
+ {
+ if (!m_scopes.count(candidates.front()))
+ return nullptr;
+ candidates = m_scopes.at(candidates.front())->resolveName(_path[i], false);
+ }
+ if (candidates.size() == 1)
+ return candidates.front();
+ else
+ return nullptr;
+}
+
+vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
+ Identifier const& _identifier,
+ vector<Declaration const*> const& _declarations
+)
+{
+ solAssert(_declarations.size() > 1, "");
+ vector<Declaration const*> uniqueFunctions;
+
+ for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
+ {
+ solAssert(*it, "");
+ // the declaration is functionDefinition while declarations > 1
+ FunctionDefinition const& functionDefinition = dynamic_cast<FunctionDefinition const&>(**it);
+ FunctionType functionType(functionDefinition);
+ for (auto parameter: functionType.parameterTypes() + functionType.returnParameterTypes())
+ if (!parameter)
+ reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context");
+
+ if (uniqueFunctions.end() == find_if(
+ uniqueFunctions.begin(),
+ uniqueFunctions.end(),
+ [&](Declaration const* d)
+ {
+ FunctionType newFunctionType(dynamic_cast<FunctionDefinition const&>(*d));
+ return functionType.hasEqualArgumentTypes(newFunctionType);
+ }
+ ))
+ uniqueFunctions.push_back(*it);
+ }
+ return uniqueFunctions;
+}
+
+void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
+{
+ auto iterator = m_scopes.find(&_base);
+ solAssert(iterator != end(m_scopes), "");
+ for (auto const& nameAndDeclaration: iterator->second->declarations())
+ for (auto const& declaration: nameAndDeclaration.second)
+ // Import if it was declared in the base, is not the constructor and is visible in derived classes
+ if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts())
+ m_currentScope->registerDeclaration(*declaration);
+}
+
+void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
+{
+ // order in the lists is from derived to base
+ // list of lists to linearize, the last element is the list of direct bases
+ list<list<ContractDefinition const*>> input(1, {});
+ for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts())
+ {
+ UserDefinedTypeName const& baseName = baseSpecifier->name();
+ auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
+ if (!base)
+ reportFatalTypeError(baseName.createTypeError("Contract expected."));
+ // "push_front" has the effect that bases mentioned later can overwrite members of bases
+ // mentioned earlier
+ input.back().push_front(base);
+ vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
+ if (basesBases.empty())
+ reportFatalTypeError(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
+ input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
+ }
+ input.back().push_front(&_contract);
+ vector<ContractDefinition const*> result = cThreeMerge(input);
+ if (result.empty())
+ reportFatalTypeError(_contract.createTypeError("Linearization of inheritance graph impossible"));
+ _contract.annotation().linearizedBaseContracts = result;
+ _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
+}
+
+template <class _T>
+vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
+{
+ // returns true iff _candidate appears only as last element of the lists
+ auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
+ {
+ for (list<_T const*> const& bases: _toMerge)
+ {
+ solAssert(!bases.empty(), "");
+ if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
+ return false;
+ }
+ return true;
+ };
+ // returns the next candidate to append to the linearized list or nullptr on failure
+ auto nextCandidate = [&]() -> _T const*
+ {
+ for (list<_T const*> const& bases: _toMerge)
+ {
+ solAssert(!bases.empty(), "");
+ if (appearsOnlyAtHead(bases.front()))
+ return bases.front();
+ }
+ return nullptr;
+ };
+ // removes the given contract from all lists
+ auto removeCandidate = [&](_T const* _candidate)
+ {
+ for (auto it = _toMerge.begin(); it != _toMerge.end();)
+ {
+ it->remove(_candidate);
+ if (it->empty())
+ it = _toMerge.erase(it);
+ else
+ ++it;
+ }
+ };
+
+ _toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
+ vector<_T const*> result;
+ while (!_toMerge.empty())
+ {
+ _T const* candidate = nextCandidate();
+ if (!candidate)
+ return vector<_T const*>();
+ result.push_back(candidate);
+ removeCandidate(candidate);
+ }
+ return result;
+}
+
+void NameAndTypeResolver::reportDeclarationError(
+ SourceLocation _sourceLoction,
+ string const& _description,
+ SourceLocation _secondarySourceLocation,
+ string const& _secondaryDescription
+)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
+ *err <<
+ errinfo_sourceLocation(_sourceLoction) <<
+ errinfo_comment(_description) <<
+ errinfo_secondarySourceLocation(
+ SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
+ );
+
+ m_errors.push_back(err);
+}
+
+void NameAndTypeResolver::reportDeclarationError(SourceLocation _sourceLocation, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
+ *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void NameAndTypeResolver::reportFatalDeclarationError(
+ SourceLocation _sourceLocation,
+ string const& _description
+)
+{
+ reportDeclarationError(_sourceLocation, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+void NameAndTypeResolver::reportTypeError(Error const& _e)
+{
+ m_errors.push_back(make_shared<Error>(_e));
+}
+
+void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
+{
+ reportTypeError(_e);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+DeclarationRegistrationHelper::DeclarationRegistrationHelper(
+ map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
+ ASTNode& _astRoot,
+ ErrorList& _errors
+):
+ m_scopes(_scopes),
+ m_currentScope(&_astRoot),
+ m_errors(_errors)
+{
+ solAssert(!!m_scopes.at(m_currentScope), "");
+ _astRoot.accept(*this);
+}
+
+bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
+{
+ SourceUnit const* importee = _import.annotation().sourceUnit;
+ solAssert(!!importee, "");
+ if (!m_scopes[importee])
+ m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
+ m_scopes[&_import] = m_scopes[importee];
+ registerDeclaration(_import, false);
+ return true;
+}
+
+bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
+{
+ registerDeclaration(_contract, true);
+ _contract.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
+{
+ registerDeclaration(_struct, true);
+ _struct.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(StructDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
+{
+ registerDeclaration(_enum, true);
+ _enum.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(EnumDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(EnumValue& _value)
+{
+ registerDeclaration(_value, false);
+ return true;
+}
+
+bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
+{
+ registerDeclaration(_function, true);
+ m_currentFunction = &_function;
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
+{
+ m_currentFunction = nullptr;
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
+{
+ registerDeclaration(_modifier, true);
+ m_currentFunction = &_modifier;
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
+{
+ m_currentFunction = nullptr;
+ closeCurrentScope();
+}
+
+void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
+{
+ // Register the local variables with the function
+ // This does not fit here perfectly, but it saves us another AST visit.
+ solAssert(m_currentFunction, "Variable declaration without function.");
+ for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations())
+ if (var)
+ m_currentFunction->addLocalVariable(*var);
+}
+
+bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
+{
+ registerDeclaration(_declaration, false);
+ return true;
+}
+
+bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
+{
+ registerDeclaration(_event, true);
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(EventDefinition&)
+{
+ closeCurrentScope();
+}
+
+void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
+{
+ map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
+ bool newlyAdded;
+ shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
+ tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
+ solAssert(newlyAdded, "Unable to add new scope.");
+ m_currentScope = &_declaration;
+}
+
+void DeclarationRegistrationHelper::closeCurrentScope()
+{
+ solAssert(m_currentScope, "Closed non-existing scope.");
+ m_currentScope = m_scopes[m_currentScope]->enclosingNode();
+}
+
+void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
+{
+ if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
+ {
+ SourceLocation firstDeclarationLocation;
+ SourceLocation secondDeclarationLocation;
+ Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration);
+ solAssert(conflictingDeclaration, "");
+
+ if (_declaration.location().start < conflictingDeclaration->location().start)
+ {
+ firstDeclarationLocation = _declaration.location();
+ secondDeclarationLocation = conflictingDeclaration->location();
+ }
+ else
+ {
+ firstDeclarationLocation = conflictingDeclaration->location();
+ secondDeclarationLocation = _declaration.location();
+ }
+
+ declarationError(
+ secondDeclarationLocation,
+ "Identifier already declared.",
+ firstDeclarationLocation,
+ "The previous declaration is here:"
+ );
+ }
+
+ _declaration.setScope(m_currentScope);
+ if (_opensScope)
+ enterNewSubScope(_declaration);
+}
+
+string DeclarationRegistrationHelper::currentCanonicalName() const
+{
+ string ret;
+ for (
+ ASTNode const* scope = m_currentScope;
+ scope != nullptr;
+ scope = m_scopes[scope]->enclosingNode()
+ )
+ {
+ if (auto decl = dynamic_cast<Declaration const*>(scope))
+ {
+ if (!ret.empty())
+ ret = "." + ret;
+ ret = decl->name() + ret;
+ }
+ }
+ return ret;
+}
+
+void DeclarationRegistrationHelper::declarationError(
+ SourceLocation _sourceLocation,
+ string const& _description,
+ SourceLocation _secondarySourceLocation,
+ string const& _secondaryDescription
+)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err <<
+ errinfo_sourceLocation(_sourceLocation) <<
+ errinfo_comment(_description) <<
+ errinfo_secondarySourceLocation(
+ SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
+ );
+
+ m_errors.push_back(err);
+}
+
+void DeclarationRegistrationHelper::declarationError(SourceLocation _sourceLocation, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void DeclarationRegistrationHelper::fatalDeclarationError(
+ SourceLocation _sourceLocation,
+ string const& _description
+)
+{
+ declarationError(_sourceLocation, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+}
+}
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
new file mode 100644
index 00000000..89b9818b
--- /dev/null
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -0,0 +1,178 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Parser part that determines the declarations corresponding to names and the types of expressions.
+ */
+
+#pragma once
+
+#include <map>
+#include <list>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/analysis/DeclarationContainer.h>
+#include <libsolidity/analysis/ReferencesResolver.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Resolves name references, typenames and sets the (explicitly given) types for all variable
+ * declarations.
+ */
+class NameAndTypeResolver: private boost::noncopyable
+{
+public:
+ NameAndTypeResolver(std::vector<Declaration const*> const& _globals, ErrorList& _errors);
+ /// Registers all declarations found in the source unit.
+ /// @returns false in case of error.
+ bool registerDeclarations(SourceUnit& _sourceUnit);
+ /// Applies the effect of import directives.
+ bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
+ /// Resolves all names and types referenced from the given contract.
+ /// @returns false in case of error.
+ bool resolveNamesAndTypes(ContractDefinition& _contract);
+ /// Updates the given global declaration (used for "this"). Not to be used with declarations
+ /// that create their own scope.
+ /// @returns false in case of error.
+ bool updateDeclaration(Declaration const& _declaration);
+
+ /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
+ /// the global scope is used (i.e. the one containing only the pre-defined global variables).
+ /// @returns a pointer to the declaration on success or nullptr on failure.
+ std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
+
+ /// Resolves a name in the "current" scope. Should only be called during the initial
+ /// resolving phase.
+ std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
+
+ /// Resolves a path starting from the "current" scope. Should only be called during the initial
+ /// resolving phase.
+ /// @note Returns a null pointer if any component in the path was not unique or not found.
+ Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
+
+ /// returns the vector of declarations without repetitions
+ std::vector<Declaration const*> cleanedDeclarations(
+ Identifier const& _identifier,
+ std::vector<Declaration const*> const& _declarations
+ );
+
+private:
+ void reset();
+
+ /// Imports all members declared directly in the given contract (i.e. does not import inherited members)
+ /// into the current scope if they are not present already.
+ void importInheritedScope(ContractDefinition const& _base);
+
+ /// Computes "C3-Linearization" of base contracts and stores it inside the contract. Reports errors if any
+ void linearizeBaseContracts(ContractDefinition& _contract);
+ /// Computes the C3-merge of the given list of lists of bases.
+ /// @returns the linearized vector or an empty vector if linearization is not possible.
+ template <class _T>
+ static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
+
+ // creates the Declaration error and adds it in the errors list
+ void reportDeclarationError(
+ SourceLocation _sourceLoction,
+ std::string const& _description,
+ SourceLocation _secondarySourceLocation,
+ std::string const& _secondaryDescription
+ );
+ // creates the Declaration error and adds it in the errors list
+ void reportDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void reportFatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+
+ // creates the Declaration error and adds it in the errors list
+ void reportTypeError(Error const& _e);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void reportFatalTypeError(Error const& _e);
+
+
+ /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
+ /// where nullptr denotes the global scope. Note that structs are not scope since they do
+ /// not contain code.
+ /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
+
+ DeclarationContainer* m_currentScope = nullptr;
+ ErrorList& m_errors;
+};
+
+/**
+ * Traverses the given AST upon construction and fills _scopes with all declarations inside the
+ * AST.
+ */
+class DeclarationRegistrationHelper: private ASTVisitor
+{
+public:
+ DeclarationRegistrationHelper(
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
+ ASTNode& _astRoot,
+ ErrorList& _errors
+ );
+
+private:
+ bool visit(ImportDirective& _declaration) override;
+ bool visit(ContractDefinition& _contract) override;
+ void endVisit(ContractDefinition& _contract) override;
+ bool visit(StructDefinition& _struct) override;
+ void endVisit(StructDefinition& _struct) override;
+ bool visit(EnumDefinition& _enum) override;
+ void endVisit(EnumDefinition& _enum) override;
+ bool visit(EnumValue& _value) override;
+ bool visit(FunctionDefinition& _function) override;
+ void endVisit(FunctionDefinition& _function) override;
+ bool visit(ModifierDefinition& _modifier) override;
+ void endVisit(ModifierDefinition& _modifier) override;
+ void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
+ bool visit(VariableDeclaration& _declaration) override;
+ bool visit(EventDefinition& _event) override;
+ void endVisit(EventDefinition& _event) override;
+
+ void enterNewSubScope(Declaration const& _declaration);
+ void closeCurrentScope();
+ void registerDeclaration(Declaration& _declaration, bool _opensScope);
+
+ /// @returns the canonical name of the current scope.
+ std::string currentCanonicalName() const;
+ // creates the Declaration error and adds it in the errors list
+ void declarationError(
+ SourceLocation _sourceLocation,
+ std::string const& _description,
+ SourceLocation _secondarySourceLocation,
+ std::string const& _secondaryDescription
+ );
+
+ // creates the Declaration error and adds it in the errors list
+ void declarationError(SourceLocation _sourceLocation, std::string const& _description);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
+ ASTNode const* m_currentScope = nullptr;
+ VariableScope* m_currentFunction = nullptr;
+ ErrorList& m_errors;
+};
+
+}
+}
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
new file mode 100644
index 00000000..ca002f58
--- /dev/null
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -0,0 +1,244 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Component that resolves type names to types and annotates the AST accordingly.
+ */
+
+#include <libsolidity/analysis/ReferencesResolver.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/analysis/ConstantEvaluator.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+bool ReferencesResolver::resolve(ASTNode const& _root)
+{
+ try
+ {
+ _root.accept(*this);
+ }
+ catch (FatalError const&)
+ {
+ solAssert(m_errorOccurred, "");
+ }
+ return !m_errorOccurred;
+}
+
+bool ReferencesResolver::visit(Identifier const& _identifier)
+{
+ auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
+ if (declarations.empty())
+ fatalDeclarationError(_identifier.location(), "Undeclared identifier.");
+ else if (declarations.size() == 1)
+ _identifier.annotation().referencedDeclaration = declarations.front();
+ else
+ _identifier.annotation().overloadedDeclarations =
+ m_resolver.cleanedDeclarations(_identifier, declarations);
+ return false;
+}
+
+bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
+{
+ _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName());
+ return true;
+}
+
+void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
+{
+ Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
+ if (!declaration)
+ fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
+
+ _typeName.annotation().referencedDeclaration = declaration;
+
+ if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<StructType>(*structDef);
+ else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<EnumType>(*enumDef);
+ else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<ContractType>(*contract);
+ else
+ fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
+}
+
+void ReferencesResolver::endVisit(Mapping const& _typeName)
+{
+ TypePointer keyType = _typeName.keyType().annotation().type;
+ TypePointer valueType = _typeName.valueType().annotation().type;
+ // Convert key type to memory.
+ keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
+ // Convert value type to storage reference.
+ valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
+ _typeName.annotation().type = make_shared<MappingType>(keyType, valueType);
+}
+
+void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
+{
+ TypePointer baseType = _typeName.baseType().annotation().type;
+ if (baseType->storageBytes() == 0)
+ fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
+ if (Expression const* length = _typeName.length())
+ {
+ if (!length->annotation().type)
+ ConstantEvaluator e(*length);
+
+ auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
+ if (!lengthType)
+ fatalTypeError(length->location(), "Invalid array length.");
+ else
+ _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
+ }
+ else
+ _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType);
+}
+
+bool ReferencesResolver::visit(Return const& _return)
+{
+ _return.annotation().functionReturnParameters = m_returnParameters;
+ return true;
+}
+
+void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
+{
+ if (_variable.annotation().type)
+ return;
+
+ TypePointer type;
+ if (_variable.typeName())
+ {
+ type = _variable.typeName()->annotation().type;
+ using Location = VariableDeclaration::Location;
+ Location varLoc = _variable.referenceLocation();
+ DataLocation typeLoc = DataLocation::Memory;
+ // References are forced to calldata for external function parameters (not return)
+ // and memory for parameters (also return) of publicly visible functions.
+ // They default to memory for function parameters and storage for local variables.
+ // As an exception, "storage" is allowed for library functions.
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+ {
+ bool isPointer = true;
+ if (_variable.isExternalCallableParameter())
+ {
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
+ if (contract.isLibrary())
+ {
+ if (varLoc == Location::Memory)
+ fatalTypeError(_variable.location(),
+ "Location has to be calldata or storage for external "
+ "library functions (remove the \"memory\" keyword)."
+ );
+ }
+ else
+ {
+ // force location of external function parameters (not return) to calldata
+ if (varLoc != Location::Default)
+ fatalTypeError(_variable.location(),
+ "Location has to be calldata for external functions "
+ "(remove the \"memory\" or \"storage\" keyword)."
+ );
+ }
+ if (varLoc == Location::Default)
+ typeLoc = DataLocation::CallData;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ }
+ else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
+ {
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
+ // force locations of public or external function (return) parameters to memory
+ if (varLoc == Location::Storage && !contract.isLibrary())
+ fatalTypeError(_variable.location(),
+ "Location has to be memory for publicly visible functions "
+ "(remove the \"storage\" keyword)."
+ );
+ if (varLoc == Location::Default || !contract.isLibrary())
+ typeLoc = DataLocation::Memory;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ }
+ else
+ {
+ if (_variable.isConstant())
+ {
+ if (varLoc != Location::Default && varLoc != Location::Memory)
+ fatalTypeError(
+ _variable.location(),
+ "Storage location has to be \"memory\" (or unspecified) for constants."
+ );
+ typeLoc = DataLocation::Memory;
+ }
+ else if (varLoc == Location::Default)
+ typeLoc = _variable.isCallableParameter() ? DataLocation::Memory : DataLocation::Storage;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ isPointer = !_variable.isStateVariable();
+ }
+
+ type = ref->copyForLocation(typeLoc, isPointer);
+ }
+ else if (varLoc != Location::Default && !ref)
+ fatalTypeError(_variable.location(), "Storage location can only be given for array or struct types.");
+
+ if (!type)
+ fatalTypeError(_variable.location(), "Invalid type name.");
+
+ }
+ else if (!_variable.canHaveAutoType())
+ fatalTypeError(_variable.location(), "Explicit type needed.");
+ // otherwise we have a "var"-declaration whose type is resolved by the first assignment
+
+ _variable.annotation().type = type;
+}
+
+void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::TypeError);
+ *err << errinfo_sourceLocation(_location) << errinfo_comment(_description);
+ m_errorOccurred = true;
+ m_errors.push_back(err);
+}
+
+void ReferencesResolver::fatalTypeError(SourceLocation const& _location, string const& _description)
+{
+ typeError(_location, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err << errinfo_sourceLocation(_location) << errinfo_comment(_description);
+ m_errorOccurred = true;
+ m_errors.push_back(err);
+}
+
+void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
+{
+ declarationError(_location, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
new file mode 100644
index 00000000..a2d71dc3
--- /dev/null
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -0,0 +1,90 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Component that resolves type names to types and annotates the AST accordingly.
+ */
+
+#pragma once
+
+#include <map>
+#include <list>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class NameAndTypeResolver;
+
+/**
+ * Resolves references to declarations (of variables and types) and also establishes the link
+ * between a return statement and the return parameter list.
+ */
+class ReferencesResolver: private ASTConstVisitor
+{
+public:
+ ReferencesResolver(
+ ErrorList& _errors,
+ NameAndTypeResolver& _resolver,
+ ParameterList const* _returnParameters,
+ bool _resolveInsideCode = false
+ ):
+ m_errors(_errors),
+ m_resolver(_resolver),
+ m_returnParameters(_returnParameters),
+ m_resolveInsideCode(_resolveInsideCode)
+ {}
+
+ /// @returns true if no errors during resolving
+ bool resolve(ASTNode const& _root);
+
+private:
+ virtual bool visit(Block const&) override { return m_resolveInsideCode; }
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual bool visit(ElementaryTypeName const& _typeName) override;
+ virtual void endVisit(UserDefinedTypeName const& _typeName) override;
+ virtual void endVisit(Mapping const& _typeName) override;
+ virtual void endVisit(ArrayTypeName const& _typeName) override;
+ virtual bool visit(Return const& _return) override;
+ virtual void endVisit(VariableDeclaration const& _variable) override;
+
+ /// Adds a new error to the list of errors.
+ void typeError(SourceLocation const& _location, std::string const& _description);
+
+ /// Adds a new error to the list of errors and throws to abort type checking.
+ void fatalTypeError(SourceLocation const& _location, std::string const& _description);
+
+ /// Adds a new error to the list of errors.
+ void declarationError(const SourceLocation& _location, std::string const& _description);
+
+ /// Adds a new error to the list of errors and throws to abort type checking.
+ void fatalDeclarationError(const SourceLocation& _location, std::string const& _description);
+
+ ErrorList& m_errors;
+ NameAndTypeResolver& m_resolver;
+ ParameterList const* m_returnParameters;
+ bool const m_resolveInsideCode;
+ bool m_errorOccurred = false;
+};
+
+}
+}
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
new file mode 100644
index 00000000..e94ce9fe
--- /dev/null
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -0,0 +1,80 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <memory>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/SyntaxChecker.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit)
+{
+ _sourceUnit.accept(*this);
+ return m_errors.empty();
+}
+
+void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::SyntaxError);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+bool SyntaxChecker::visit(WhileStatement const&)
+{
+ m_inLoopDepth++;
+ return true;
+}
+
+void SyntaxChecker::endVisit(WhileStatement const&)
+{
+ m_inLoopDepth--;
+}
+
+bool SyntaxChecker::visit(ForStatement const&)
+{
+ m_inLoopDepth++;
+ return true;
+}
+
+void SyntaxChecker::endVisit(ForStatement const&)
+{
+ m_inLoopDepth--;
+}
+
+bool SyntaxChecker::visit(Continue const& _continueStatement)
+{
+ if (m_inLoopDepth <= 0)
+ // we're not in a for/while loop, report syntax error
+ syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
+ return true;
+}
+
+bool SyntaxChecker::visit(Break const& _breakStatement)
+{
+ if (m_inLoopDepth <= 0)
+ // we're not in a for/while loop, report syntax error
+ syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
+ return true;
+}
+
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
new file mode 100644
index 00000000..c836d49f
--- /dev/null
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -0,0 +1,61 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * The module that performs syntax analysis on the AST:
+ * - whether continue/break is in a for/while loop.
+ */
+class SyntaxChecker: private ASTConstVisitor
+{
+public:
+ /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ SyntaxChecker(ErrorList& _errors): m_errors(_errors) {}
+
+ bool checkSyntax(SourceUnit const& _sourceUnit);
+
+private:
+ /// Adds a new error to the list of errors.
+ void syntaxError(SourceLocation const& _location, std::string const& _description);
+
+ virtual bool visit(WhileStatement const& _whileStatement) override;
+ virtual void endVisit(WhileStatement const& _whileStatement) override;
+ virtual bool visit(ForStatement const& _forStatement) override;
+ virtual void endVisit(ForStatement const& _forStatement) override;
+
+ virtual bool visit(Continue const& _continueStatement) override;
+ virtual bool visit(Break const& _breakStatement) override;
+
+ ErrorList& m_errors;
+
+ int m_inLoopDepth = 0;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
new file mode 100644
index 00000000..44f4629b
--- /dev/null
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -0,0 +1,1472 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Type analyzer and checker.
+ */
+
+#include <libsolidity/analysis/TypeChecker.h>
+#include <memory>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
+{
+ try
+ {
+ visit(_contract);
+ }
+ catch (FatalError const&)
+ {
+ // We got a fatal error which required to stop further type checking, but we can
+ // continue normally from here.
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ }
+ return Error::containsOnlyWarnings(m_errors);
+}
+
+TypePointer const& TypeChecker::type(Expression const& _expression) const
+{
+ solAssert(!!_expression.annotation().type, "Type requested but not present.");
+ return _expression.annotation().type;
+}
+
+TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
+{
+ solAssert(!!_variable.annotation().type, "Type requested but not present.");
+ return _variable.annotation().type;
+}
+
+bool TypeChecker::visit(ContractDefinition const& _contract)
+{
+ m_scope = &_contract;
+
+ // We force our own visiting order here.
+ //@TODO structs will be visited again below, but it is probably fine.
+ ASTNode::listAccept(_contract.definedStructs(), *this);
+ ASTNode::listAccept(_contract.baseContracts(), *this);
+
+ checkContractDuplicateFunctions(_contract);
+ checkContractIllegalOverrides(_contract);
+ checkContractAbstractFunctions(_contract);
+ checkContractAbstractConstructors(_contract);
+
+ FunctionDefinition const* function = _contract.constructor();
+ if (function && !function->returnParameters().empty())
+ typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
+
+ FunctionDefinition const* fallbackFunction = nullptr;
+ for (FunctionDefinition const* function: _contract.definedFunctions())
+ {
+ if (function->name().empty())
+ {
+ if (fallbackFunction)
+ {
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err << errinfo_comment("Only one fallback function is allowed.");
+ m_errors.push_back(err);
+ }
+ else
+ {
+ fallbackFunction = function;
+ if (!fallbackFunction->parameters().empty())
+ typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
+ }
+ }
+ if (!function->isImplemented())
+ _contract.annotation().isFullyImplemented = false;
+ }
+
+ ASTNode::listAccept(_contract.subNodes(), *this);
+
+ checkContractExternalTypeClashes(_contract);
+ // check for hash collisions in function signatures
+ set<FixedHash<4>> hashes;
+ for (auto const& it: _contract.interfaceFunctionList())
+ {
+ FixedHash<4> const& hash = it.first;
+ if (hashes.count(hash))
+ typeError(
+ _contract.location(),
+ string("Function signature hash collision for ") + it.second->externalSignature()
+ );
+ hashes.insert(hash);
+ }
+
+ if (_contract.isLibrary())
+ checkLibraryRequirements(_contract);
+
+ return false;
+}
+
+void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
+{
+ /// Checks that two functions with the same name defined in this contract have different
+ /// argument types and that there is at most one constructor.
+ map<string, vector<FunctionDefinition const*>> functions;
+ for (FunctionDefinition const* function: _contract.definedFunctions())
+ functions[function->name()].push_back(function);
+
+ // Constructor
+ if (functions[_contract.name()].size() > 1)
+ {
+ SecondarySourceLocation ssl;
+ auto it = ++functions[_contract.name()].begin();
+ for (; it != functions[_contract.name()].end(); ++it)
+ ssl.append("Another declaration is here:", (*it)->location());
+
+ auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
+ *err <<
+ errinfo_sourceLocation(functions[_contract.name()].front()->location()) <<
+ errinfo_comment("More than one constructor defined.") <<
+ errinfo_secondarySourceLocation(ssl);
+ m_errors.push_back(err);
+ }
+ for (auto const& it: functions)
+ {
+ vector<FunctionDefinition const*> const& overloads = it.second;
+ for (size_t i = 0; i < overloads.size(); ++i)
+ for (size_t j = i + 1; j < overloads.size(); ++j)
+ if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j])))
+ {
+ auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
+ *err <<
+ errinfo_sourceLocation(overloads[j]->location()) <<
+ errinfo_comment("Function with same name and arguments defined twice.") <<
+ errinfo_secondarySourceLocation(SecondarySourceLocation().append(
+ "Other declaration is here:", overloads[i]->location()));
+ m_errors.push_back(err);
+ }
+ }
+}
+
+void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract)
+{
+ // Mapping from name to function definition (exactly one per argument type equality class) and
+ // flag to indicate whether it is fully implemented.
+ using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
+ map<string, vector<FunTypeAndFlag>> functions;
+
+ // Search from base to derived
+ for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
+ for (FunctionDefinition const* function: contract->definedFunctions())
+ {
+ auto& overloads = functions[function->name()];
+ FunctionTypePointer funType = make_shared<FunctionType>(*function);
+ auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
+ {
+ return funType->hasEqualArgumentTypes(*_funAndFlag.first);
+ });
+ if (it == overloads.end())
+ overloads.push_back(make_pair(funType, function->isImplemented()));
+ else if (it->second)
+ {
+ if (!function->isImplemented())
+ typeError(function->location(), "Redeclaring an already implemented function as abstract");
+ }
+ else if (function->isImplemented())
+ it->second = true;
+ }
+
+ // Set to not fully implemented if at least one flag is false.
+ for (auto const& it: functions)
+ for (auto const& funAndFlag: it.second)
+ if (!funAndFlag.second)
+ {
+ _contract.annotation().isFullyImplemented = false;
+ return;
+ }
+}
+
+void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
+{
+ set<ContractDefinition const*> argumentsNeeded;
+ // check that we get arguments for all base constructors that need it.
+ // If not mark the contract as abstract (not fully implemented)
+
+ vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
+ for (ContractDefinition const* contract: bases)
+ if (FunctionDefinition const* constructor = contract->constructor())
+ if (contract != &_contract && !constructor->parameters().empty())
+ argumentsNeeded.insert(contract);
+
+ for (ContractDefinition const* contract: bases)
+ {
+ if (FunctionDefinition const* constructor = contract->constructor())
+ for (auto const& modifier: constructor->modifiers())
+ {
+ auto baseContract = dynamic_cast<ContractDefinition const*>(
+ &dereference(*modifier->name())
+ );
+ if (baseContract)
+ argumentsNeeded.erase(baseContract);
+ }
+
+
+ for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
+ {
+ auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
+ solAssert(baseContract, "");
+ if (!base->arguments().empty())
+ argumentsNeeded.erase(baseContract);
+ }
+ }
+ if (!argumentsNeeded.empty())
+ _contract.annotation().isFullyImplemented = false;
+}
+
+void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
+{
+ // TODO unify this at a later point. for this we need to put the constness and the access specifier
+ // into the types
+ map<string, vector<FunctionDefinition const*>> functions;
+ map<string, ModifierDefinition const*> modifiers;
+
+ // We search from derived to base, so the stored item causes the error.
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (FunctionDefinition const* function: contract->definedFunctions())
+ {
+ if (function->isConstructor())
+ continue; // constructors can neither be overridden nor override anything
+ string const& name = function->name();
+ if (modifiers.count(name))
+ typeError(modifiers[name]->location(), "Override changes function to modifier.");
+ FunctionType functionType(*function);
+ // function should not change the return type
+ for (FunctionDefinition const* overriding: functions[name])
+ {
+ FunctionType overridingType(*overriding);
+ if (!overridingType.hasEqualArgumentTypes(functionType))
+ continue;
+ if (
+ overriding->visibility() != function->visibility() ||
+ overriding->isDeclaredConst() != function->isDeclaredConst() ||
+ overridingType != functionType
+ )
+ typeError(overriding->location(), "Override changes extended function signature.");
+ }
+ functions[name].push_back(function);
+ }
+ for (ModifierDefinition const* modifier: contract->functionModifiers())
+ {
+ string const& name = modifier->name();
+ ModifierDefinition const*& override = modifiers[name];
+ if (!override)
+ override = modifier;
+ else if (ModifierType(*override) != ModifierType(*modifier))
+ typeError(override->location(), "Override changes modifier signature.");
+ if (!functions[name].empty())
+ typeError(override->location(), "Override changes modifier to function.");
+ }
+ }
+}
+
+void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
+{
+ map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (FunctionDefinition const* f: contract->definedFunctions())
+ if (f->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*f);
+ // under non error circumstances this should be true
+ if (functionType->interfaceFunctionType())
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(f, functionType)
+ );
+ }
+ for (VariableDeclaration const* v: contract->stateVariables())
+ if (v->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*v);
+ // under non error circumstances this should be true
+ if (functionType->interfaceFunctionType())
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(v, functionType)
+ );
+ }
+ }
+ for (auto const& it: externalDeclarations)
+ for (size_t i = 0; i < it.second.size(); ++i)
+ for (size_t j = i + 1; j < it.second.size(); ++j)
+ if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second))
+ typeError(
+ it.second[j].first->location(),
+ "Function overload clash during conversion to external types for arguments."
+ );
+}
+
+void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
+{
+ solAssert(_contract.isLibrary(), "");
+ if (!_contract.baseContracts().empty())
+ typeError(_contract.location(), "Library is not allowed to inherit.");
+
+ for (auto const& var: _contract.stateVariables())
+ if (!var->isConstant())
+ typeError(var->location(), "Library cannot have non-constant state variables");
+}
+
+void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
+{
+ auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
+ solAssert(base, "Base contract not available.");
+
+ if (base->isLibrary())
+ typeError(_inheritance.location(), "Libraries cannot be inherited from.");
+
+ auto const& arguments = _inheritance.arguments();
+ TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
+ if (!arguments.empty() && parameterTypes.size() != arguments.size())
+ {
+ typeError(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+ return;
+ }
+
+ for (size_t i = 0; i < arguments.size(); ++i)
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ typeError(
+ arguments[i]->location(),
+ "Invalid type for argument in constructor call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+}
+
+void TypeChecker::endVisit(UsingForDirective const& _usingFor)
+{
+ ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
+ _usingFor.libraryName().annotation().referencedDeclaration
+ );
+ if (!library || !library->isLibrary())
+ typeError(_usingFor.libraryName().location(), "Library name expected.");
+}
+
+bool TypeChecker::visit(StructDefinition const& _struct)
+{
+ for (ASTPointer<VariableDeclaration> const& member: _struct.members())
+ if (!type(*member)->canBeStored())
+ typeError(member->location(), "Type cannot be used in struct.");
+
+ // Check recursion, fatal error if detected.
+ using StructPointer = StructDefinition const*;
+ using StructPointersSet = set<StructPointer>;
+ function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
+ {
+ if (_parents.count(_struct))
+ fatalTypeError(_struct->location(), "Recursive struct definition.");
+ StructPointersSet parents = _parents;
+ parents.insert(_struct);
+ for (ASTPointer<VariableDeclaration> const& member: _struct->members())
+ if (type(*member)->category() == Type::Category::Struct)
+ {
+ auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName());
+ check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents);
+ }
+ };
+ check(&_struct, StructPointersSet{});
+
+ ASTNode::listAccept(_struct.members(), *this);
+
+ return false;
+}
+
+bool TypeChecker::visit(FunctionDefinition const& _function)
+{
+ bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
+ for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
+ {
+ if (!type(*var)->canLiveOutsideStorage())
+ typeError(var->location(), "Type is required to live outside storage.");
+ if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
+ fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
+ }
+ for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
+ visitManually(
+ *modifier,
+ _function.isConstructor() ?
+ dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
+ vector<ContractDefinition const*>()
+ );
+ if (_function.isImplemented())
+ _function.body().accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(VariableDeclaration const& _variable)
+{
+ // Variables can be declared without type (with "var"), in which case the first assignment
+ // sets the type.
+ // Note that assignments before the first declaration are legal because of the special scoping
+ // rules inherited from JavaScript.
+
+ // type is filled either by ReferencesResolver directly from the type name or by
+ // TypeChecker at the VariableDeclarationStatement level.
+ TypePointer varType = _variable.annotation().type;
+ solAssert(!!varType, "Failed to infer variable type.");
+ if (_variable.isConstant())
+ {
+ if (!dynamic_cast<ContractDefinition const*>(_variable.scope()))
+ typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
+ if (!_variable.value())
+ typeError(_variable.location(), "Uninitialized \"constant\" variable.");
+ if (!varType->isValueType())
+ {
+ bool constImplemented = false;
+ if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
+ constImplemented = arrayType->isByteArray();
+ if (!constImplemented)
+ typeError(
+ _variable.location(),
+ "Illegal use of \"constant\" specifier. \"constant\" "
+ "is not yet implemented for this type."
+ );
+ }
+ }
+ if (_variable.value())
+ expectType(*_variable.value(), *varType);
+ if (!_variable.isStateVariable())
+ {
+ if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
+ if (!varType->canLiveOutsideStorage())
+ typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage.");
+ }
+ else if (
+ _variable.visibility() >= VariableDeclaration::Visibility::Public &&
+ !FunctionType(_variable).interfaceFunctionType()
+ )
+ typeError(_variable.location(), "Internal type is not allowed for public state variables.");
+ return false;
+}
+
+void TypeChecker::visitManually(
+ ModifierInvocation const& _modifier,
+ vector<ContractDefinition const*> const& _bases
+)
+{
+ std::vector<ASTPointer<Expression>> const& arguments = _modifier.arguments();
+ for (ASTPointer<Expression> const& argument: arguments)
+ argument->accept(*this);
+ _modifier.name()->accept(*this);
+
+ auto const* declaration = &dereference(*_modifier.name());
+ vector<ASTPointer<VariableDeclaration>> emptyParameterList;
+ vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
+ if (auto modifierDecl = dynamic_cast<ModifierDefinition const*>(declaration))
+ parameters = &modifierDecl->parameters();
+ else
+ // check parameters for Base constructors
+ for (ContractDefinition const* base: _bases)
+ if (declaration == base)
+ {
+ if (auto referencedConstructor = base->constructor())
+ parameters = &referencedConstructor->parameters();
+ else
+ parameters = &emptyParameterList;
+ break;
+ }
+ if (!parameters)
+ {
+ typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class.");
+ return;
+ }
+ if (parameters->size() != arguments.size())
+ {
+ typeError(
+ _modifier.location(),
+ "Wrong argument count for modifier invocation: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameters->size()) +
+ "."
+ );
+ return;
+ }
+ for (size_t i = 0; i < _modifier.arguments().size(); ++i)
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])))
+ typeError(
+ arguments[i]->location(),
+ "Invalid type for argument in modifier invocation. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ type(*(*parameters)[i])->toString() +
+ " requested."
+ );
+}
+
+bool TypeChecker::visit(EventDefinition const& _eventDef)
+{
+ unsigned numIndexed = 0;
+ for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
+ {
+ if (var->isIndexed())
+ numIndexed++;
+ if (_eventDef.isAnonymous() && numIndexed > 4)
+ typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
+ else if (!_eventDef.isAnonymous() && numIndexed > 3)
+ typeError(_eventDef.location(), "More than 3 indexed arguments for event.");
+ if (!type(*var)->canLiveOutsideStorage())
+ typeError(var->location(), "Type is required to live outside storage.");
+ if (!type(*var)->interfaceType(false))
+ typeError(var->location(), "Internal type is not allowed as event parameter type.");
+ }
+ return false;
+}
+
+
+bool TypeChecker::visit(IfStatement const& _ifStatement)
+{
+ expectType(_ifStatement.condition(), BoolType());
+ _ifStatement.trueStatement().accept(*this);
+ if (_ifStatement.falseStatement())
+ _ifStatement.falseStatement()->accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(WhileStatement const& _whileStatement)
+{
+ expectType(_whileStatement.condition(), BoolType());
+ _whileStatement.body().accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(ForStatement const& _forStatement)
+{
+ if (_forStatement.initializationExpression())
+ _forStatement.initializationExpression()->accept(*this);
+ if (_forStatement.condition())
+ expectType(*_forStatement.condition(), BoolType());
+ if (_forStatement.loopExpression())
+ _forStatement.loopExpression()->accept(*this);
+ _forStatement.body().accept(*this);
+ return false;
+}
+
+void TypeChecker::endVisit(Return const& _return)
+{
+ if (!_return.expression())
+ return;
+ ParameterList const* params = _return.annotation().functionReturnParameters;
+ if (!params)
+ {
+ typeError(_return.location(), "Return arguments not allowed.");
+ return;
+ }
+ TypePointers returnTypes;
+ for (auto const& var: params->parameters())
+ returnTypes.push_back(type(*var));
+ if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get()))
+ {
+ if (tupleType->components().size() != params->parameters().size())
+ typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
+ else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)))
+ typeError(
+ _return.expression()->location(),
+ "Return argument type " +
+ type(*_return.expression())->toString() +
+ " is not implicitly convertible to expected type " +
+ TupleType(returnTypes).toString(false) +
+ "."
+ );
+ }
+ else if (params->parameters().size() != 1)
+ typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
+ else
+ {
+ TypePointer const& expected = type(*params->parameters().front());
+ if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
+ typeError(
+ _return.expression()->location(),
+ "Return argument type " +
+ type(*_return.expression())->toString() +
+ " is not implicitly convertible to expected type (type of first return variable) " +
+ expected->toString() +
+ "."
+ );
+ }
+}
+
+bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
+{
+ if (!_statement.initialValue())
+ {
+ // No initial value is only permitted for single variables with specified type.
+ if (_statement.declarations().size() != 1 || !_statement.declarations().front())
+ fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
+ VariableDeclaration const& varDecl = *_statement.declarations().front();
+ if (!varDecl.annotation().type)
+ fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
+ if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
+ {
+ if (ref->dataStoredIn(DataLocation::Storage))
+ {
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err <<
+ errinfo_sourceLocation(varDecl.location()) <<
+ errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?");
+ m_errors.push_back(err);
+ }
+ }
+ varDecl.accept(*this);
+ return false;
+ }
+
+ // Here we have an initial value and might have to derive some types before we can visit
+ // the variable declaration(s).
+
+ _statement.initialValue()->accept(*this);
+ TypePointers valueTypes;
+ if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue()).get()))
+ valueTypes = tupleType->components();
+ else
+ valueTypes = TypePointers{type(*_statement.initialValue())};
+
+ // Determine which component is assigned to which variable.
+ // If numbers do not match, fill up if variables begin or end empty (not both).
+ vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
+ assignments.resize(valueTypes.size(), nullptr);
+ vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
+ if (variables.empty())
+ {
+ if (!valueTypes.empty())
+ fatalTypeError(
+ _statement.location(),
+ "Too many components (" +
+ toString(valueTypes.size()) +
+ ") in value for variable assignment (0) needed"
+ );
+ }
+ else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
+ fatalTypeError(
+ _statement.location(),
+ "Wildcard both at beginning and end of variable declaration list is only allowed "
+ "if the number of components is equal."
+ );
+ size_t minNumValues = variables.size();
+ if (!variables.empty() && (!variables.back() || !variables.front()))
+ --minNumValues;
+ if (valueTypes.size() < minNumValues)
+ fatalTypeError(
+ _statement.location(),
+ "Not enough components (" +
+ toString(valueTypes.size()) +
+ ") in value to assign all variables (" +
+ toString(minNumValues) + ")."
+ );
+ if (valueTypes.size() > variables.size() && variables.front() && variables.back())
+ fatalTypeError(
+ _statement.location(),
+ "Too many components (" +
+ toString(valueTypes.size()) +
+ ") in value for variable assignment (" +
+ toString(minNumValues) +
+ " needed)."
+ );
+ bool fillRight = !variables.empty() && (!variables.back() || variables.front());
+ for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
+ if (fillRight)
+ assignments[i] = variables[i].get();
+ else
+ assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
+
+ for (size_t i = 0; i < assignments.size(); ++i)
+ {
+ if (!assignments[i])
+ continue;
+ VariableDeclaration const& var = *assignments[i];
+ solAssert(!var.value(), "Value has to be tied to statement.");
+ TypePointer const& valueComponentType = valueTypes[i];
+ solAssert(!!valueComponentType, "");
+ if (!var.annotation().type)
+ {
+ // Infer type from value.
+ solAssert(!var.typeName(), "");
+ if (
+ valueComponentType->category() == Type::Category::IntegerConstant &&
+ !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType()
+ )
+ fatalTypeError(_statement.initialValue()->location(), "Invalid integer constant " + valueComponentType->toString() + ".");
+ var.annotation().type = valueComponentType->mobileType();
+ var.accept(*this);
+ }
+ else
+ {
+ var.accept(*this);
+ if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
+ typeError(
+ _statement.location(),
+ "Type " +
+ valueComponentType->toString() +
+ " is not implicitly convertible to expected type " +
+ var.annotation().type->toString() +
+ "."
+ );
+ }
+ }
+ return false;
+}
+
+void TypeChecker::endVisit(ExpressionStatement const& _statement)
+{
+ if (type(_statement.expression())->category() == Type::Category::IntegerConstant)
+ if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType())
+ typeError(_statement.expression().location(), "Invalid integer constant.");
+}
+
+bool TypeChecker::visit(Conditional const& _conditional)
+{
+ expectType(_conditional.condition(), BoolType());
+
+ _conditional.trueExpression().accept(*this);
+ _conditional.falseExpression().accept(*this);
+
+ TypePointer trueType = type(_conditional.trueExpression())->mobileType();
+ TypePointer falseType = type(_conditional.falseExpression())->mobileType();
+
+ TypePointer commonType = Type::commonType(trueType, falseType);
+ if (!commonType)
+ {
+ typeError(
+ _conditional.location(),
+ "True expression's type " +
+ trueType->toString() +
+ " doesn't match false expression's type " +
+ falseType->toString() +
+ "."
+ );
+ // even we can't find a common type, we have to set a type here,
+ // otherwise the upper statement will not be able to check the type.
+ commonType = trueType;
+ }
+
+ _conditional.annotation().type = commonType;
+
+ if (_conditional.annotation().lValueRequested)
+ typeError(
+ _conditional.location(),
+ "Conditional expression as left value is not supported yet."
+ );
+
+ return false;
+}
+
+bool TypeChecker::visit(Assignment const& _assignment)
+{
+ requireLValue(_assignment.leftHandSide());
+ TypePointer t = type(_assignment.leftHandSide());
+ _assignment.annotation().type = t;
+ if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get()))
+ {
+ // Sequenced assignments of tuples is not valid, make the result a "void" type.
+ _assignment.annotation().type = make_shared<TupleType>();
+ expectType(_assignment.rightHandSide(), *tupleType);
+ }
+ else if (t->category() == Type::Category::Mapping)
+ {
+ typeError(_assignment.location(), "Mappings cannot be assigned to.");
+ _assignment.rightHandSide().accept(*this);
+ }
+ else if (_assignment.assignmentOperator() == Token::Assign)
+ expectType(_assignment.rightHandSide(), *t);
+ else
+ {
+ // compound assignment
+ _assignment.rightHandSide().accept(*this);
+ TypePointer resultType = t->binaryOperatorResult(
+ Token::AssignmentToBinaryOp(_assignment.assignmentOperator()),
+ type(_assignment.rightHandSide())
+ );
+ if (!resultType || *resultType != *t)
+ typeError(
+ _assignment.location(),
+ "Operator " +
+ string(Token::toString(_assignment.assignmentOperator())) +
+ " not compatible with types " +
+ t->toString() +
+ " and " +
+ type(_assignment.rightHandSide())->toString()
+ );
+ }
+ return false;
+}
+
+bool TypeChecker::visit(TupleExpression const& _tuple)
+{
+ vector<ASTPointer<Expression>> const& components = _tuple.components();
+ TypePointers types;
+
+ if (_tuple.annotation().lValueRequested)
+ {
+ if (_tuple.isInlineArray())
+ fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
+ for (auto const& component: components)
+ if (component)
+ {
+ requireLValue(*component);
+ types.push_back(type(*component));
+ }
+ else
+ types.push_back(TypePointer());
+ if (components.size() == 1)
+ _tuple.annotation().type = type(*components[0]);
+ else
+ _tuple.annotation().type = make_shared<TupleType>(types);
+ // If some of the components are not LValues, the error is reported above.
+ _tuple.annotation().isLValue = true;
+ }
+ else
+ {
+ TypePointer inlineArrayType;
+ for (size_t i = 0; i < components.size(); ++i)
+ {
+ // Outside of an lvalue-context, the only situation where a component can be empty is (x,).
+ if (!components[i] && !(i == 1 && components.size() == 2))
+ fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
+ else if (components[i])
+ {
+ components[i]->accept(*this);
+ types.push_back(type(*components[i]));
+ if (_tuple.isInlineArray())
+ solAssert(!!types[i], "Inline array cannot have empty components");
+ if (i == 0 && _tuple.isInlineArray())
+ inlineArrayType = types[i]->mobileType();
+ else if (_tuple.isInlineArray() && inlineArrayType)
+ inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
+ }
+ else
+ types.push_back(TypePointer());
+ }
+ if (_tuple.isInlineArray())
+ {
+ if (!inlineArrayType)
+ fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
+ _tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
+ }
+ else
+ {
+ if (components.size() == 1)
+ _tuple.annotation().type = type(*components[0]);
+ else
+ {
+ if (components.size() == 2 && !components[1])
+ types.pop_back();
+ _tuple.annotation().type = make_shared<TupleType>(types);
+ }
+ }
+
+ }
+ return false;
+}
+
+bool TypeChecker::visit(UnaryOperation const& _operation)
+{
+ // Inc, Dec, Add, Sub, Not, BitNot, Delete
+ Token::Value op = _operation.getOperator();
+ if (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete)
+ requireLValue(_operation.subExpression());
+ else
+ _operation.subExpression().accept(*this);
+ TypePointer const& subExprType = type(_operation.subExpression());
+ TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op);
+ if (!t)
+ {
+ typeError(
+ _operation.location(),
+ "Unary operator " +
+ string(Token::toString(op)) +
+ " cannot be applied to type " +
+ subExprType->toString()
+ );
+ t = subExprType;
+ }
+ _operation.annotation().type = t;
+ return false;
+}
+
+void TypeChecker::endVisit(BinaryOperation const& _operation)
+{
+ TypePointer const& leftType = type(_operation.leftExpression());
+ TypePointer const& rightType = type(_operation.rightExpression());
+ TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ if (!commonType)
+ {
+ typeError(
+ _operation.location(),
+ "Operator " +
+ string(Token::toString(_operation.getOperator())) +
+ " not compatible with types " +
+ leftType->toString() +
+ " and " +
+ rightType->toString()
+ );
+ commonType = leftType;
+ }
+ _operation.annotation().commonType = commonType;
+ _operation.annotation().type =
+ Token::isCompareOp(_operation.getOperator()) ?
+ make_shared<BoolType>() :
+ commonType;
+}
+
+bool TypeChecker::visit(FunctionCall const& _functionCall)
+{
+ bool isPositionalCall = _functionCall.names().empty();
+ vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
+ vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names();
+
+ // We need to check arguments' type first as they will be needed for overload resolution.
+ shared_ptr<TypePointers> argumentTypes;
+ if (isPositionalCall)
+ argumentTypes = make_shared<TypePointers>();
+ for (ASTPointer<Expression const> const& argument: arguments)
+ {
+ argument->accept(*this);
+ // only store them for positional calls
+ if (isPositionalCall)
+ argumentTypes->push_back(type(*argument));
+ }
+ if (isPositionalCall)
+ _functionCall.expression().annotation().argumentTypes = move(argumentTypes);
+
+ _functionCall.expression().accept(*this);
+ TypePointer expressionType = type(_functionCall.expression());
+
+ if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get()))
+ {
+ _functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct);
+ _functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall;
+ }
+ else
+ _functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false;
+
+ if (_functionCall.annotation().isTypeConversion)
+ {
+ TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
+ TypePointer resultType = t.actualType();
+ if (arguments.size() != 1)
+ typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion.");
+ else if (!isPositionalCall)
+ typeError(_functionCall.location(), "Type conversion cannot allow named arguments.");
+ else
+ {
+ TypePointer const& argType = type(*arguments.front());
+ if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
+ // do not change the data location when converting
+ // (data location cannot yet be specified for type conversions)
+ resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
+ if (!argType->isExplicitlyConvertibleTo(*resultType))
+ typeError(_functionCall.location(), "Explicit type conversion not allowed.");
+ }
+ _functionCall.annotation().type = resultType;
+
+ return false;
+ }
+
+ // Actual function call or struct constructor call.
+
+ FunctionTypePointer functionType;
+
+ /// For error message: Struct members that were removed during conversion to memory.
+ set<string> membersRemovedForStructConstructor;
+ if (_functionCall.annotation().isStructConstructorCall)
+ {
+ TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
+ auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
+ functionType = structType.constructorType();
+ membersRemovedForStructConstructor = structType.membersMissingInMemory();
+ }
+ else
+ functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
+
+ if (!functionType)
+ {
+ typeError(_functionCall.location(), "Type is not callable");
+ _functionCall.annotation().type = make_shared<TupleType>();
+ return false;
+ }
+ else if (functionType->returnParameterTypes().size() == 1)
+ _functionCall.annotation().type = functionType->returnParameterTypes().front();
+ else
+ _functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes());
+
+ TypePointers parameterTypes = functionType->parameterTypes();
+ if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
+ {
+ string msg =
+ "Wrong argument count for function call: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ ".";
+ // Extend error message in case we try to construct a struct with mapping member.
+ if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty())
+ {
+ msg += " Members that have to be skipped in memory:";
+ for (auto const& member: membersRemovedForStructConstructor)
+ msg += " " + member;
+ }
+ typeError(_functionCall.location(), msg);
+ }
+ else if (isPositionalCall)
+ {
+ // call by positional arguments
+ for (size_t i = 0; i < arguments.size(); ++i)
+ {
+ auto const& argType = type(*arguments[i]);
+ if (functionType->takesArbitraryParameters())
+ {
+ if (auto t = dynamic_cast<IntegerConstantType const*>(argType.get()))
+ if (!t->integerType())
+ typeError(arguments[i]->location(), "Integer constant too large.");
+ }
+ else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ typeError(
+ arguments[i]->location(),
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ }
+ }
+ else
+ {
+ // call by named arguments
+ auto const& parameterNames = functionType->parameterNames();
+ if (functionType->takesArbitraryParameters())
+ typeError(
+ _functionCall.location(),
+ "Named arguments cannnot be used for functions that take arbitrary parameters."
+ );
+ else if (parameterNames.size() > argumentNames.size())
+ typeError(_functionCall.location(), "Some argument names are missing.");
+ else if (parameterNames.size() < argumentNames.size())
+ typeError(_functionCall.location(), "Too many arguments.");
+ else
+ {
+ // check duplicate names
+ bool duplication = false;
+ for (size_t i = 0; i < argumentNames.size(); i++)
+ for (size_t j = i + 1; j < argumentNames.size(); j++)
+ if (*argumentNames[i] == *argumentNames[j])
+ {
+ duplication = true;
+ typeError(arguments[i]->location(), "Duplicate named argument.");
+ }
+
+ // check actual types
+ if (!duplication)
+ for (size_t i = 0; i < argumentNames.size(); i++)
+ {
+ bool found = false;
+ for (size_t j = 0; j < parameterNames.size(); j++)
+ if (parameterNames[j] == *argumentNames[i])
+ {
+ found = true;
+ // check type convertible
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j]))
+ typeError(
+ arguments[i]->location(),
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ break;
+ }
+
+ if (!found)
+ typeError(
+ _functionCall.location(),
+ "Named argument does not match function declaration."
+ );
+ }
+ }
+ }
+
+ return false;
+}
+
+void TypeChecker::endVisit(NewExpression const& _newExpression)
+{
+ TypePointer type = _newExpression.typeName().annotation().type;
+ solAssert(!!type, "Type name not resolved.");
+
+ if (auto contractName = dynamic_cast<UserDefinedTypeName const*>(&_newExpression.typeName()))
+ {
+ auto contract = dynamic_cast<ContractDefinition const*>(&dereference(*contractName));
+
+ if (!contract)
+ fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
+ if (!contract->annotation().isFullyImplemented)
+ typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
+
+ solAssert(!!m_scope, "");
+ m_scope->annotation().contractDependencies.insert(contract);
+ solAssert(
+ !contract->annotation().linearizedBaseContracts.empty(),
+ "Linearized base contracts not yet available."
+ );
+ if (contractDependenciesAreCyclic(*m_scope))
+ typeError(
+ _newExpression.location(),
+ "Circular reference for contract creation (cannot create instance of derived or same contract)."
+ );
+
+ auto contractType = make_shared<ContractType>(*contract);
+ TypePointers parameterTypes = contractType->constructorType()->parameterTypes();
+ _newExpression.annotation().type = make_shared<FunctionType>(
+ parameterTypes,
+ TypePointers{contractType},
+ strings(),
+ strings(),
+ FunctionType::Location::Creation
+ );
+ }
+ else if (type->category() == Type::Category::Array)
+ {
+ if (!type->canLiveOutsideStorage())
+ fatalTypeError(
+ _newExpression.typeName().location(),
+ "Type cannot live outside storage."
+ );
+ if (!type->isDynamicallySized())
+ typeError(
+ _newExpression.typeName().location(),
+ "Length has to be placed in parentheses after the array type for new expression."
+ );
+ type = ReferenceType::copyForLocationIfReference(DataLocation::Memory, type);
+ _newExpression.annotation().type = make_shared<FunctionType>(
+ TypePointers{make_shared<IntegerType>(256)},
+ TypePointers{type},
+ strings(),
+ strings(),
+ FunctionType::Location::ObjectCreation
+ );
+ }
+ else
+ fatalTypeError(_newExpression.location(), "Contract or array type expected.");
+}
+
+bool TypeChecker::visit(MemberAccess const& _memberAccess)
+{
+ _memberAccess.expression().accept(*this);
+ TypePointer exprType = type(_memberAccess.expression());
+ ASTString const& memberName = _memberAccess.memberName();
+
+ // Retrieve the types of the arguments if this is used to call a function.
+ auto const& argumentTypes = _memberAccess.annotation().argumentTypes;
+ MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
+ if (possibleMembers.size() > 1 && argumentTypes)
+ {
+ // do overload resolution
+ for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
+ if (
+ it->type->category() == Type::Category::Function &&
+ !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes, exprType)
+ )
+ it = possibleMembers.erase(it);
+ else
+ ++it;
+ }
+ if (possibleMembers.size() == 0)
+ {
+ auto storageType = ReferenceType::copyForLocationIfReference(
+ DataLocation::Storage,
+ exprType
+ );
+ if (!storageType->members(m_scope).membersByName(memberName).empty())
+ fatalTypeError(
+ _memberAccess.location(),
+ "Member \"" + memberName + "\" is not available in " +
+ exprType->toString() +
+ " outside of storage."
+ );
+ fatalTypeError(
+ _memberAccess.location(),
+ "Member \"" + memberName + "\" not found or not visible "
+ "after argument-dependent lookup in " + exprType->toString()
+ );
+ }
+ else if (possibleMembers.size() > 1)
+ fatalTypeError(
+ _memberAccess.location(),
+ "Member \"" + memberName + "\" not unique "
+ "after argument-dependent lookup in " + exprType->toString()
+ );
+
+ auto& annotation = _memberAccess.annotation();
+ annotation.referencedDeclaration = possibleMembers.front().declaration;
+ annotation.type = possibleMembers.front().type;
+
+ if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get()))
+ if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType()))
+ typeError(
+ _memberAccess.location(),
+ "Function \"" + memberName + "\" cannot be called on an object of type " +
+ exprType->toString() + " (expected " + funType->selfType()->toString() + ")"
+ );
+
+ if (exprType->category() == Type::Category::Struct)
+ annotation.isLValue = true;
+ else if (exprType->category() == Type::Category::Array)
+ {
+ auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
+ annotation.isLValue = (
+ memberName == "length" &&
+ arrayType.location() == DataLocation::Storage &&
+ arrayType.isDynamicallySized()
+ );
+ }
+ else if (exprType->category() == Type::Category::FixedBytes)
+ annotation.isLValue = false;
+
+ return false;
+}
+
+bool TypeChecker::visit(IndexAccess const& _access)
+{
+ _access.baseExpression().accept(*this);
+ TypePointer baseType = type(_access.baseExpression());
+ TypePointer resultType;
+ bool isLValue = false;
+ Expression const* index = _access.indexExpression();
+ switch (baseType->category())
+ {
+ case Type::Category::Array:
+ {
+ ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
+ if (!index)
+ typeError(_access.location(), "Index expression cannot be omitted.");
+ else if (actualType.isString())
+ {
+ typeError(_access.location(), "Index access for string is not possible.");
+ index->accept(*this);
+ }
+ else
+ {
+ expectType(*index, IntegerType(256));
+ if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr))
+ typeError(_access.location(), "Out of bounds array access.");
+ }
+ resultType = actualType.baseType();
+ isLValue = actualType.location() != DataLocation::CallData;
+ break;
+ }
+ case Type::Category::Mapping:
+ {
+ MappingType const& actualType = dynamic_cast<MappingType const&>(*baseType);
+ if (!index)
+ typeError(_access.location(), "Index expression cannot be omitted.");
+ else
+ expectType(*index, *actualType.keyType());
+ resultType = actualType.valueType();
+ isLValue = true;
+ break;
+ }
+ case Type::Category::TypeType:
+ {
+ TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType);
+ if (!index)
+ resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
+ else
+ {
+ index->accept(*this);
+ if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ resultType = make_shared<TypeType>(make_shared<ArrayType>(
+ DataLocation::Memory,
+ typeType.actualType(),
+ length->literalValue(nullptr)
+ ));
+ else
+ typeError(index->location(), "Integer constant expected.");
+ }
+ break;
+ }
+ case Type::Category::FixedBytes:
+ {
+ FixedBytesType const& bytesType = dynamic_cast<FixedBytesType const&>(*baseType);
+ if (!index)
+ typeError(_access.location(), "Index expression cannot be omitted.");
+ else
+ {
+ expectType(*index, IntegerType(256));
+ if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ if (bytesType.numBytes() <= integerType->literalValue(nullptr))
+ typeError(_access.location(), "Out of bounds array access.");
+ }
+ resultType = make_shared<FixedBytesType>(1);
+ isLValue = false; // @todo this heavily depends on how it is embedded
+ break;
+ }
+ default:
+ fatalTypeError(
+ _access.baseExpression().location(),
+ "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")"
+ );
+ }
+ _access.annotation().type = move(resultType);
+ _access.annotation().isLValue = isLValue;
+
+ return false;
+}
+
+bool TypeChecker::visit(Identifier const& _identifier)
+{
+ IdentifierAnnotation& annotation = _identifier.annotation();
+ if (!annotation.referencedDeclaration)
+ {
+ if (!annotation.argumentTypes)
+ fatalTypeError(_identifier.location(), "Unable to determine overloaded type.");
+ if (annotation.overloadedDeclarations.empty())
+ fatalTypeError(_identifier.location(), "No candidates for overload resolution found.");
+ else if (annotation.overloadedDeclarations.size() == 1)
+ annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin();
+ else
+ {
+ vector<Declaration const*> candidates;
+
+ for (Declaration const* declaration: annotation.overloadedDeclarations)
+ {
+ TypePointer function = declaration->type();
+ solAssert(!!function, "Requested type not present.");
+ auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
+ if (functionType && functionType->canTakeArguments(*annotation.argumentTypes))
+ candidates.push_back(declaration);
+ }
+ if (candidates.empty())
+ fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
+ else if (candidates.size() == 1)
+ annotation.referencedDeclaration = candidates.front();
+ else
+ fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
+ }
+ }
+ solAssert(
+ !!annotation.referencedDeclaration,
+ "Referenced declaration is null after overload resolution."
+ );
+ annotation.isLValue = annotation.referencedDeclaration->isLValue();
+ annotation.type = annotation.referencedDeclaration->type();
+ if (!annotation.type)
+ fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
+ return false;
+}
+
+void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
+{
+ _expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeName()));
+}
+
+void TypeChecker::endVisit(Literal const& _literal)
+{
+ _literal.annotation().type = Type::forLiteral(_literal);
+ if (!_literal.annotation().type)
+ fatalTypeError(_literal.location(), "Invalid literal value.");
+}
+
+bool TypeChecker::contractDependenciesAreCyclic(
+ ContractDefinition const& _contract,
+ std::set<ContractDefinition const*> const& _seenContracts
+) const
+{
+ // Naive depth-first search that remembers nodes already seen.
+ if (_seenContracts.count(&_contract))
+ return true;
+ set<ContractDefinition const*> seen(_seenContracts);
+ seen.insert(&_contract);
+ for (auto const* c: _contract.annotation().contractDependencies)
+ if (contractDependenciesAreCyclic(*c, seen))
+ return true;
+ return false;
+}
+
+Declaration const& TypeChecker::dereference(Identifier const& _identifier) const
+{
+ solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored.");
+ return *_identifier.annotation().referencedDeclaration;
+}
+
+Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName) const
+{
+ solAssert(!!_typeName.annotation().referencedDeclaration, "Declaration not stored.");
+ return *_typeName.annotation().referencedDeclaration;
+}
+
+void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
+{
+ _expression.accept(*this);
+
+ if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
+ typeError(
+ _expression.location(),
+ "Type " +
+ type(_expression)->toString() +
+ " is not implicitly convertible to expected type " +
+ _expectedType.toString() +
+ "."
+ );
+}
+
+void TypeChecker::requireLValue(Expression const& _expression)
+{
+ _expression.annotation().lValueRequested = true;
+ _expression.accept(*this);
+ if (!_expression.annotation().isLValue)
+ typeError(_expression.location(), "Expression has to be an lvalue.");
+}
+
+void TypeChecker::typeError(SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::TypeError);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void TypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
+{
+ typeError(_location, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
new file mode 100644
index 00000000..b884db49
--- /dev/null
+++ b/libsolidity/analysis/TypeChecker.h
@@ -0,0 +1,128 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Type analyzer and checker.
+ */
+
+#pragma once
+
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+
+/**
+ * The module that performs type analysis on the AST, checks the applicability of operations on
+ * those types and stores errors for invalid operations.
+ * Provides a way to retrieve the type of an AST node.
+ */
+class TypeChecker: private ASTConstVisitor
+{
+public:
+ /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ TypeChecker(ErrorList& _errors): m_errors(_errors) {}
+
+ /// Performs type checking on the given contract and all of its sub-nodes.
+ /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
+ bool checkTypeRequirements(ContractDefinition const& _contract);
+
+ /// @returns the type of an expression and asserts that it is present.
+ TypePointer const& type(Expression const& _expression) const;
+ /// @returns the type of the given variable and throws if the type is not present
+ /// (this can happen for variables with non-explicit types before their types are resolved)
+ TypePointer const& type(VariableDeclaration const& _variable) const;
+
+private:
+ /// Adds a new error to the list of errors.
+ void typeError(SourceLocation const& _location, std::string const& _description);
+
+ /// Adds a new error to the list of errors and throws to abort type checking.
+ void fatalTypeError(SourceLocation const& _location, std::string const& _description);
+
+ virtual bool visit(ContractDefinition const& _contract) override;
+ /// Checks that two functions defined in this contract with the same name have different
+ /// arguments and that there is at most one constructor.
+ void checkContractDuplicateFunctions(ContractDefinition const& _contract);
+ void checkContractIllegalOverrides(ContractDefinition const& _contract);
+ void checkContractAbstractFunctions(ContractDefinition const& _contract);
+ void checkContractAbstractConstructors(ContractDefinition const& _contract);
+ /// Checks that different functions with external visibility end up having different
+ /// external argument types (i.e. different signature).
+ void checkContractExternalTypeClashes(ContractDefinition const& _contract);
+ /// Checks that all requirements for a library are fulfilled if this is a library.
+ void checkLibraryRequirements(ContractDefinition const& _contract);
+
+ virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
+ virtual void endVisit(UsingForDirective const& _usingFor) override;
+ virtual bool visit(StructDefinition const& _struct) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+ virtual bool visit(VariableDeclaration const& _variable) override;
+ /// We need to do this manually because we want to pass the bases of the current contract in
+ /// case this is a base constructor call.
+ void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
+ virtual bool visit(EventDefinition const& _eventDef) override;
+ virtual bool visit(IfStatement const& _ifStatement) override;
+ virtual bool visit(WhileStatement const& _whileStatement) override;
+ virtual bool visit(ForStatement const& _forStatement) override;
+ virtual void endVisit(Return const& _return) override;
+ virtual bool visit(VariableDeclarationStatement const& _variable) override;
+ virtual void endVisit(ExpressionStatement const& _statement) override;
+ virtual bool visit(Conditional const& _conditional) override;
+ virtual bool visit(Assignment const& _assignment) override;
+ virtual bool visit(TupleExpression const& _tuple) override;
+ virtual void endVisit(BinaryOperation const& _operation) override;
+ virtual bool visit(UnaryOperation const& _operation) override;
+ virtual bool visit(FunctionCall const& _functionCall) override;
+ virtual void endVisit(NewExpression const& _newExpression) override;
+ virtual bool visit(MemberAccess const& _memberAccess) override;
+ virtual bool visit(IndexAccess const& _indexAccess) override;
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
+ virtual void endVisit(Literal const& _literal) override;
+
+ bool contractDependenciesAreCyclic(
+ ContractDefinition const& _contract,
+ std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
+ ) const;
+
+ /// @returns the referenced declaration and throws on error.
+ Declaration const& dereference(Identifier const& _identifier) const;
+ /// @returns the referenced declaration and throws on error.
+ Declaration const& dereference(UserDefinedTypeName const& _typeName) const;
+
+ /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly
+ /// convertible to @a _expectedType.
+ void expectType(Expression const& _expression, Type const& _expectedType);
+ /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
+ void requireLValue(Expression const& _expression);
+
+ ContractDefinition const* m_scope = nullptr;
+
+ ErrorList& m_errors;
+};
+
+}
+}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
new file mode 100644
index 00000000..b5affa8e
--- /dev/null
+++ b/libsolidity/ast/AST.cpp
@@ -0,0 +1,413 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity abstract syntax tree.
+ */
+
+#include <algorithm>
+#include <functional>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/ast/AST_accept.h>
+
+#include <libdevcore/SHA3.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+ASTNode::ASTNode(SourceLocation const& _location):
+ m_location(_location)
+{
+}
+
+ASTNode::~ASTNode()
+{
+ delete m_annotation;
+}
+
+ASTAnnotation& ASTNode::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ASTAnnotation();
+ return *m_annotation;
+}
+
+Error ASTNode::createTypeError(string const& _description) const
+{
+ return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description);
+}
+
+SourceUnitAnnotation& SourceUnit::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new SourceUnitAnnotation();
+ return static_cast<SourceUnitAnnotation&>(*m_annotation);
+}
+
+ImportAnnotation& ImportDirective::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ImportAnnotation();
+ return static_cast<ImportAnnotation&>(*m_annotation);
+}
+
+TypePointer ImportDirective::type() const
+{
+ solAssert(!!annotation().sourceUnit, "");
+ return make_shared<ModuleType>(*annotation().sourceUnit);
+}
+
+map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
+{
+ auto exportedFunctionList = interfaceFunctionList();
+
+ map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
+ for (auto const& it: exportedFunctionList)
+ exportedFunctions.insert(it);
+
+ solAssert(
+ exportedFunctionList.size() == exportedFunctions.size(),
+ "Hash collision at Function Definition Hash calculation"
+ );
+
+ return exportedFunctions;
+}
+
+FunctionDefinition const* ContractDefinition::constructor() const
+{
+ for (FunctionDefinition const* f: definedFunctions())
+ if (f->isConstructor())
+ return f;
+ return nullptr;
+}
+
+FunctionDefinition const* ContractDefinition::fallbackFunction() const
+{
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
+ for (FunctionDefinition const* f: contract->definedFunctions())
+ if (f->name().empty())
+ return f;
+ return nullptr;
+}
+
+vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
+{
+ if (!m_interfaceEvents)
+ {
+ set<string> eventsSeen;
+ m_interfaceEvents.reset(new vector<EventDefinition const*>());
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
+ for (EventDefinition const* e: contract->events())
+ if (eventsSeen.count(e->name()) == 0)
+ {
+ eventsSeen.insert(e->name());
+ m_interfaceEvents->push_back(e);
+ }
+ }
+ return *m_interfaceEvents;
+}
+
+vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList() const
+{
+ if (!m_interfaceFunctionList)
+ {
+ set<string> functionsSeen;
+ set<string> signaturesSeen;
+ m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
+ {
+ vector<FunctionTypePointer> functions;
+ for (FunctionDefinition const* f: contract->definedFunctions())
+ if (f->isPartOfExternalInterface())
+ functions.push_back(make_shared<FunctionType>(*f, false));
+ for (VariableDeclaration const* v: contract->stateVariables())
+ if (v->isPartOfExternalInterface())
+ functions.push_back(make_shared<FunctionType>(*v));
+ for (FunctionTypePointer const& fun: functions)
+ {
+ if (!fun->interfaceFunctionType())
+ // Fails hopefully because we already registered the error
+ continue;
+ string functionSignature = fun->externalSignature();
+ if (signaturesSeen.count(functionSignature) == 0)
+ {
+ signaturesSeen.insert(functionSignature);
+ FixedHash<4> hash(dev::sha3(functionSignature));
+ m_interfaceFunctionList->push_back(make_pair(hash, fun));
+ }
+ }
+ }
+ }
+ return *m_interfaceFunctionList;
+}
+
+string const& ContractDefinition::devDocumentation() const
+{
+ return m_devDocumentation;
+}
+
+string const& ContractDefinition::userDocumentation() const
+{
+ return m_userDocumentation;
+}
+
+void ContractDefinition::setDevDocumentation(string const& _devDocumentation)
+{
+ m_devDocumentation = _devDocumentation;
+}
+
+void ContractDefinition::setUserDocumentation(string const& _userDocumentation)
+{
+ m_userDocumentation = _userDocumentation;
+}
+
+
+vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
+{
+ if (!m_inheritableMembers)
+ {
+ set<string> memberSeen;
+ m_inheritableMembers.reset(new vector<Declaration const*>());
+ auto addInheritableMember = [&](Declaration const* _decl)
+ {
+ if (memberSeen.count(_decl->name()) == 0 && _decl->isVisibleInDerivedContracts())
+ {
+ memberSeen.insert(_decl->name());
+ m_inheritableMembers->push_back(_decl);
+ }
+ };
+
+ for (FunctionDefinition const* f: definedFunctions())
+ addInheritableMember(f);
+
+ for (VariableDeclaration const* v: stateVariables())
+ addInheritableMember(v);
+
+ for (StructDefinition const* s: definedStructs())
+ addInheritableMember(s);
+ }
+ return *m_inheritableMembers;
+}
+
+TypePointer ContractDefinition::type() const
+{
+ return make_shared<TypeType>(make_shared<ContractType>(*this));
+}
+
+ContractDefinitionAnnotation& ContractDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ContractDefinitionAnnotation();
+ return static_cast<ContractDefinitionAnnotation&>(*m_annotation);
+}
+
+TypeNameAnnotation& TypeName::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new TypeNameAnnotation();
+ return static_cast<TypeNameAnnotation&>(*m_annotation);
+}
+
+TypePointer StructDefinition::type() const
+{
+ return make_shared<TypeType>(make_shared<StructType>(*this));
+}
+
+TypeDeclarationAnnotation& StructDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new TypeDeclarationAnnotation();
+ return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
+}
+
+TypePointer EnumValue::type() const
+{
+ auto parentDef = dynamic_cast<EnumDefinition const*>(scope());
+ solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
+ return make_shared<EnumType>(*parentDef);
+}
+
+TypePointer EnumDefinition::type() const
+{
+ return make_shared<TypeType>(make_shared<EnumType>(*this));
+}
+
+TypeDeclarationAnnotation& EnumDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new TypeDeclarationAnnotation();
+ return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
+}
+
+TypePointer FunctionDefinition::type() const
+{
+ return make_shared<FunctionType>(*this);
+}
+
+string FunctionDefinition::externalSignature() const
+{
+ return FunctionType(*this).externalSignature();
+}
+
+FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new FunctionDefinitionAnnotation();
+ return static_cast<FunctionDefinitionAnnotation&>(*m_annotation);
+}
+
+TypePointer ModifierDefinition::type() const
+{
+ return make_shared<ModifierType>(*this);
+}
+
+ModifierDefinitionAnnotation& ModifierDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ModifierDefinitionAnnotation();
+ return static_cast<ModifierDefinitionAnnotation&>(*m_annotation);
+}
+
+TypePointer EventDefinition::type() const
+{
+ return make_shared<FunctionType>(*this);
+}
+
+EventDefinitionAnnotation& EventDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new EventDefinitionAnnotation();
+ return static_cast<EventDefinitionAnnotation&>(*m_annotation);
+}
+
+UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new UserDefinedTypeNameAnnotation();
+ return static_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
+}
+
+bool VariableDeclaration::isLValue() const
+{
+ // External function parameters and constant declared variables are Read-Only
+ return !isExternalCallableParameter() && !m_isConstant;
+}
+
+bool VariableDeclaration::isCallableParameter() const
+{
+ auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
+ if (!callable)
+ return false;
+ for (auto const& variable: callable->parameters())
+ if (variable.get() == this)
+ return true;
+ if (callable->returnParameterList())
+ for (auto const& variable: callable->returnParameterList()->parameters())
+ if (variable.get() == this)
+ return true;
+ return false;
+}
+
+bool VariableDeclaration::isExternalCallableParameter() const
+{
+ auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
+ if (!callable || callable->visibility() != Declaration::Visibility::External)
+ return false;
+ for (auto const& variable: callable->parameters())
+ if (variable.get() == this)
+ return true;
+ return false;
+}
+
+bool VariableDeclaration::canHaveAutoType() const
+{
+ auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
+ return (!!callable && !isCallableParameter());
+}
+
+TypePointer VariableDeclaration::type() const
+{
+ return annotation().type;
+}
+
+VariableDeclarationAnnotation& VariableDeclaration::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new VariableDeclarationAnnotation();
+ return static_cast<VariableDeclarationAnnotation&>(*m_annotation);
+}
+
+StatementAnnotation& Statement::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new StatementAnnotation();
+ return static_cast<StatementAnnotation&>(*m_annotation);
+}
+
+ReturnAnnotation& Return::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ReturnAnnotation();
+ return static_cast<ReturnAnnotation&>(*m_annotation);
+}
+
+VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new VariableDeclarationStatementAnnotation();
+ return static_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
+}
+
+ExpressionAnnotation& Expression::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ExpressionAnnotation();
+ return static_cast<ExpressionAnnotation&>(*m_annotation);
+}
+
+MemberAccessAnnotation& MemberAccess::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new MemberAccessAnnotation();
+ return static_cast<MemberAccessAnnotation&>(*m_annotation);
+}
+
+BinaryOperationAnnotation& BinaryOperation::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new BinaryOperationAnnotation();
+ return static_cast<BinaryOperationAnnotation&>(*m_annotation);
+}
+
+FunctionCallAnnotation& FunctionCall::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new FunctionCallAnnotation();
+ return static_cast<FunctionCallAnnotation&>(*m_annotation);
+}
+
+IdentifierAnnotation& Identifier::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new IdentifierAnnotation();
+ return static_cast<IdentifierAnnotation&>(*m_annotation);
+}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
new file mode 100644
index 00000000..d32d76a4
--- /dev/null
+++ b/libsolidity/ast/AST.h
@@ -0,0 +1,1468 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity abstract syntax tree.
+ */
+
+#pragma once
+
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <boost/noncopyable.hpp>
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/parsing/Token.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ASTVisitor;
+class ASTConstVisitor;
+
+
+/**
+ * The root (abstract) class of the AST inheritance tree.
+ * It is possible to traverse all direct and indirect children of an AST node by calling
+ * accept, providing an ASTVisitor.
+ */
+class ASTNode: private boost::noncopyable
+{
+public:
+ explicit ASTNode(SourceLocation const& _location);
+ virtual ~ASTNode();
+
+ virtual void accept(ASTVisitor& _visitor) = 0;
+ virtual void accept(ASTConstVisitor& _visitor) const = 0;
+ template <class T>
+ static void listAccept(std::vector<T> const& _list, ASTVisitor& _visitor)
+ {
+ for (T const& element: _list)
+ element->accept(_visitor);
+ }
+ template <class T>
+ static void listAccept(std::vector<T> const& _list, ASTConstVisitor& _visitor)
+ {
+ for (T const& element: _list)
+ element->accept(_visitor);
+ }
+
+ /// @returns a copy of the vector containing only the nodes which derive from T.
+ template <class _T>
+ static std::vector<_T const*> filteredNodes(std::vector<ASTPointer<ASTNode>> const& _nodes);
+
+ /// Returns the source code location of this node.
+ SourceLocation const& location() const { return m_location; }
+
+ /// Creates a @ref TypeError exception and decorates it with the location of the node and
+ /// the given description
+ Error createTypeError(std::string const& _description) const;
+
+ ///@todo make this const-safe by providing a different way to access the annotation
+ virtual ASTAnnotation& annotation() const;
+
+ ///@{
+ ///@name equality operators
+ /// Equality relies on the fact that nodes cannot be copied.
+ bool operator==(ASTNode const& _other) const { return this == &_other; }
+ bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
+ ///@}
+
+protected:
+ /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism).
+ mutable ASTAnnotation* m_annotation = nullptr;
+
+private:
+ SourceLocation m_location;
+};
+
+template <class _T>
+std::vector<_T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> const& _nodes)
+{
+ std::vector<_T const*> ret;
+ for (auto const& n: _nodes)
+ if (auto const* nt = dynamic_cast<_T const*>(n.get()))
+ ret.push_back(nt);
+ return ret;
+}
+
+/**
+ * Source unit containing import directives and contract definitions.
+ */
+class SourceUnit: public ASTNode
+{
+public:
+ SourceUnit(SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
+ ASTNode(_location), m_nodes(_nodes) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+ virtual SourceUnitAnnotation& annotation() const override;
+
+ std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
+
+private:
+ std::vector<ASTPointer<ASTNode>> m_nodes;
+};
+
+/**
+ * Abstract AST class for a declaration (contract, function, struct, variable, import directive).
+ */
+class Declaration: public ASTNode
+{
+public:
+ /// Visibility ordered from restricted to unrestricted.
+ enum class Visibility { Default, Private, Internal, Public, External };
+
+ Declaration(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ Visibility _visibility = Visibility::Default
+ ):
+ ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
+
+ /// @returns the declared name.
+ ASTString const& name() const { return *m_name; }
+ Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; }
+ bool isPublic() const { return visibility() >= Visibility::Public; }
+ virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
+ bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
+
+ /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
+ /// Available only after name and type resolution step.
+ ASTNode const* scope() const { return m_scope; }
+ void setScope(ASTNode const* _scope) { m_scope = _scope; }
+
+ virtual bool isLValue() const { return false; }
+ virtual bool isPartOfExternalInterface() const { return false; }
+
+ /// @returns the type of expressions referencing this declaration.
+ /// The current contract has to be given since this context can change the type, especially of
+ /// contract types.
+ /// This can only be called once types of variable declarations have already been resolved.
+ virtual TypePointer type() const = 0;
+
+protected:
+ virtual Visibility defaultVisibility() const { return Visibility::Public; }
+
+private:
+ ASTPointer<ASTString> m_name;
+ Visibility m_visibility;
+ ASTNode const* m_scope;
+};
+
+/**
+ * Import directive for referencing other files / source objects.
+ * Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope
+ * Source objects are identified by a string which can be a file name but does not have to be.
+ * Other ways to use it:
+ * import "abc" as x; // creates symbol "x" that contains all symbols in "abc"
+ * import * as x from "abc"; // same as above
+ * import {a as b, c} from "abc"; // creates new symbols "b" and "c" referencing "a" and "c" in "abc", respectively.
+ */
+class ImportDirective: public Declaration
+{
+public:
+ ImportDirective(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _path,
+ ASTPointer<ASTString> const& _unitAlias,
+ std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
+ ):
+ Declaration(_location, _unitAlias),
+ m_path(_path),
+ m_symbolAliases(_symbolAliases)
+ { }
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ASTString const& path() const { return *m_path; }
+ std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> const& symbolAliases() const
+ {
+ return m_symbolAliases;
+ }
+ virtual ImportAnnotation& annotation() const override;
+
+ virtual TypePointer type() const override;
+
+private:
+ ASTPointer<ASTString> m_path;
+ /// The aliases for the specific symbols to import. If non-empty import the specific symbols.
+ /// If the second component is empty, import the identifier unchanged.
+ /// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
+ std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases;
+};
+
+/**
+ * Abstract class that is added to each AST node that can store local variables.
+ */
+class VariableScope
+{
+public:
+ void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
+ std::vector<VariableDeclaration const*> const& localVariables() const { return m_localVariables; }
+
+private:
+ std::vector<VariableDeclaration const*> m_localVariables;
+};
+
+/**
+ * Abstract class that is added to each AST node that can receive documentation.
+ */
+class Documented
+{
+public:
+ explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
+
+ /// @return A shared pointer of an ASTString.
+ /// Can contain a nullptr in which case indicates absence of documentation
+ ASTPointer<ASTString> const& documentation() const { return m_documentation; }
+
+protected:
+ ASTPointer<ASTString> m_documentation;
+};
+
+/**
+ * Abstract class that is added to AST nodes that can be marked as not being fully implemented
+ */
+class ImplementationOptional
+{
+public:
+ explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
+
+ /// @return whether this node is fully implemented or not
+ bool isImplemented() const { return m_implemented; }
+
+protected:
+ bool m_implemented;
+};
+
+/// @}
+
+/**
+ * Definition of a contract or library. This is the only AST nodes where child nodes are not visited in
+ * document order. It first visits all struct declarations, then all variable declarations and
+ * finally all function declarations.
+ */
+class ContractDefinition: public Declaration, public Documented
+{
+public:
+ ContractDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ ASTPointer<ASTString> const& _documentation,
+ std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
+ std::vector<ASTPointer<ASTNode>> const& _subNodes,
+ bool _isLibrary
+ ):
+ Declaration(_location, _name),
+ Documented(_documentation),
+ m_baseContracts(_baseContracts),
+ m_subNodes(_subNodes),
+ m_isLibrary(_isLibrary)
+ {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; }
+ std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
+ std::vector<UsingForDirective const*> usingForDirectives() const { return filteredNodes<UsingForDirective>(m_subNodes); }
+ std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); }
+ std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); }
+ std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); }
+ std::vector<ModifierDefinition const*> functionModifiers() const { return filteredNodes<ModifierDefinition>(m_subNodes); }
+ std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
+ std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
+ std::vector<EventDefinition const*> const& interfaceEvents() const;
+ bool isLibrary() const { return m_isLibrary; }
+
+ /// @returns a map of canonical function signatures to FunctionDefinitions
+ /// as intended for use by the ABI.
+ std::map<FixedHash<4>, FunctionTypePointer> interfaceFunctions() const;
+ std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
+
+ /// @returns a list of the inheritable members of this contract
+ std::vector<Declaration const*> const& inheritableMembers() const;
+
+ /// Returns the constructor or nullptr if no constructor was specified.
+ FunctionDefinition const* constructor() const;
+ /// Returns the fallback function or nullptr if no fallback function was specified.
+ FunctionDefinition const* fallbackFunction() const;
+
+ std::string const& userDocumentation() const;
+ void setUserDocumentation(std::string const& _userDocumentation);
+
+ std::string const& devDocumentation() const;
+ void setDevDocumentation(std::string const& _devDocumentation);
+
+ virtual TypePointer type() const override;
+
+ virtual ContractDefinitionAnnotation& annotation() const override;
+
+private:
+ std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
+ std::vector<ASTPointer<ASTNode>> m_subNodes;
+ bool m_isLibrary;
+
+ // parsed Natspec documentation of the contract.
+ std::string m_userDocumentation;
+ std::string m_devDocumentation;
+
+ std::vector<ContractDefinition const*> m_linearizedBaseContracts;
+ mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
+ mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
+ mutable std::unique_ptr<std::vector<Declaration const*>> m_inheritableMembers;
+};
+
+class InheritanceSpecifier: public ASTNode
+{
+public:
+ InheritanceSpecifier(
+ SourceLocation const& _location,
+ ASTPointer<UserDefinedTypeName> const& _baseName,
+ std::vector<ASTPointer<Expression>> _arguments
+ ):
+ ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ UserDefinedTypeName const& name() const { return *m_baseName; }
+ std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
+
+private:
+ ASTPointer<UserDefinedTypeName> m_baseName;
+ std::vector<ASTPointer<Expression>> m_arguments;
+};
+
+/**
+ * `using LibraryName for uint` will attach all functions from the library LibraryName
+ * to `uint` if the first parameter matches the type. `using LibraryName for *` attaches
+ * the function to any matching type.
+ */
+class UsingForDirective: public ASTNode
+{
+public:
+ UsingForDirective(
+ SourceLocation const& _location,
+ ASTPointer<UserDefinedTypeName> const& _libraryName,
+ ASTPointer<TypeName> const& _typeName
+ ):
+ ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ UserDefinedTypeName const& libraryName() const { return *m_libraryName; }
+ /// @returns the type name the library is attached to, null for `*`.
+ TypeName const* typeName() const { return m_typeName.get(); }
+
+private:
+ ASTPointer<UserDefinedTypeName> m_libraryName;
+ ASTPointer<TypeName> m_typeName;
+};
+
+class StructDefinition: public Declaration
+{
+public:
+ StructDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ std::vector<ASTPointer<VariableDeclaration>> const& _members
+ ):
+ Declaration(_location, _name), m_members(_members) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
+
+ virtual TypePointer type() const override;
+
+ virtual TypeDeclarationAnnotation& annotation() const override;
+
+private:
+ std::vector<ASTPointer<VariableDeclaration>> m_members;
+};
+
+class EnumDefinition: public Declaration
+{
+public:
+ EnumDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ std::vector<ASTPointer<EnumValue>> const& _members
+ ):
+ Declaration(_location, _name), m_members(_members) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; }
+
+ virtual TypePointer type() const override;
+
+ virtual TypeDeclarationAnnotation& annotation() const override;
+
+private:
+ std::vector<ASTPointer<EnumValue>> m_members;
+};
+
+/**
+ * Declaration of an Enum Value
+ */
+class EnumValue: public Declaration
+{
+public:
+ EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
+ Declaration(_location, _name) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ virtual TypePointer type() const override;
+};
+
+/**
+ * Parameter list, used as function parameter list and return list.
+ * None of the parameters is allowed to contain mappings (not even recursively
+ * inside structs).
+ */
+class ParameterList: public ASTNode
+{
+public:
+ ParameterList(
+ SourceLocation const& _location,
+ std::vector<ASTPointer<VariableDeclaration>> const& _parameters
+ ):
+ ASTNode(_location), m_parameters(_parameters) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters; }
+
+private:
+ std::vector<ASTPointer<VariableDeclaration>> m_parameters;
+};
+
+/**
+ * Base class for all nodes that define function-like objects, i.e. FunctionDefinition,
+ * EventDefinition and ModifierDefinition.
+ */
+class CallableDeclaration: public Declaration, public VariableScope
+{
+public:
+ CallableDeclaration(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ Declaration::Visibility _visibility,
+ ASTPointer<ParameterList> const& _parameters,
+ ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>()
+ ):
+ Declaration(_location, _name, _visibility),
+ m_parameters(_parameters),
+ m_returnParameters(_returnParameters)
+ {
+ }
+
+ std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters->parameters(); }
+ ParameterList const& parameterList() const { return *m_parameters; }
+ ASTPointer<ParameterList> const& returnParameterList() const { return m_returnParameters; }
+
+protected:
+ ASTPointer<ParameterList> m_parameters;
+ ASTPointer<ParameterList> m_returnParameters;
+};
+
+class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional
+{
+public:
+ FunctionDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ Declaration::Visibility _visibility,
+ bool _isConstructor,
+ ASTPointer<ASTString> const& _documentation,
+ ASTPointer<ParameterList> const& _parameters,
+ bool _isDeclaredConst,
+ std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
+ ASTPointer<ParameterList> const& _returnParameters,
+ ASTPointer<Block> const& _body
+ ):
+ CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
+ Documented(_documentation),
+ ImplementationOptional(_body != nullptr),
+ m_isConstructor(_isConstructor),
+ m_isDeclaredConst(_isDeclaredConst),
+ m_functionModifiers(_modifiers),
+ m_body(_body)
+ {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ bool isConstructor() const { return m_isConstructor; }
+ bool isDeclaredConst() const { return m_isDeclaredConst; }
+ std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
+ std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
+ Block const& body() const { return *m_body; }
+
+ virtual bool isVisibleInContract() const override
+ {
+ return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
+ }
+ virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); }
+
+ /// @returns the external signature of the function
+ /// That consists of the name of the function followed by the types of the
+ /// arguments separated by commas all enclosed in parentheses without any spaces.
+ std::string externalSignature() const;
+
+ virtual TypePointer type() const override;
+
+ virtual FunctionDefinitionAnnotation& annotation() const override;
+
+private:
+ bool m_isConstructor;
+ bool m_isDeclaredConst;
+ std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
+ ASTPointer<Block> m_body;
+};
+
+/**
+ * Declaration of a variable. This can be used in various places, e.g. in function parameter
+ * lists, struct definitions and even function bodys.
+ */
+class VariableDeclaration: public Declaration
+{
+public:
+ enum Location { Default, Storage, Memory };
+
+ VariableDeclaration(
+ SourceLocation const& _sourceLocation,
+ ASTPointer<TypeName> const& _type,
+ ASTPointer<ASTString> const& _name,
+ ASTPointer<Expression> _value,
+ Visibility _visibility,
+ bool _isStateVar = false,
+ bool _isIndexed = false,
+ bool _isConstant = false,
+ Location _referenceLocation = Location::Default
+ ):
+ Declaration(_sourceLocation, _name, _visibility),
+ m_typeName(_type),
+ m_value(_value),
+ m_isStateVariable(_isStateVar),
+ m_isIndexed(_isIndexed),
+ m_isConstant(_isConstant),
+ m_location(_referenceLocation) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ TypeName* typeName() const { return m_typeName.get(); }
+ ASTPointer<Expression> const& value() const { return m_value; }
+
+ virtual bool isLValue() const override;
+ virtual bool isPartOfExternalInterface() const override { return isPublic(); }
+
+ bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); }
+ /// @returns true if this variable is a parameter or return parameter of a function.
+ bool isCallableParameter() const;
+ /// @returns true if this variable is a parameter (not return parameter) of an external function.
+ bool isExternalCallableParameter() const;
+ /// @returns true if the type of the variable does not need to be specified, i.e. it is declared
+ /// in the body of a function or modifier.
+ bool canHaveAutoType() const;
+ bool isStateVariable() const { return m_isStateVariable; }
+ bool isIndexed() const { return m_isIndexed; }
+ bool isConstant() const { return m_isConstant; }
+ Location referenceLocation() const { return m_location; }
+
+ virtual TypePointer type() const override;
+
+ virtual VariableDeclarationAnnotation& annotation() const override;
+
+protected:
+ Visibility defaultVisibility() const override { return Visibility::Internal; }
+
+private:
+ ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
+ /// Initially assigned value, can be missing. For local variables, this is stored inside
+ /// VariableDeclarationStatement and not here.
+ ASTPointer<Expression> m_value;
+ bool m_isStateVariable; ///< Whether or not this is a contract state variable
+ bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
+ bool m_isConstant; ///< Whether the variable is a compile-time constant.
+ Location m_location; ///< Location of the variable if it is of reference type.
+};
+
+/**
+ * Definition of a function modifier.
+ */
+class ModifierDefinition: public CallableDeclaration, public Documented
+{
+public:
+ ModifierDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ ASTPointer<ASTString> const& _documentation,
+ ASTPointer<ParameterList> const& _parameters,
+ ASTPointer<Block> const& _body
+ ):
+ CallableDeclaration(_location, _name, Visibility::Default, _parameters),
+ Documented(_documentation),
+ m_body(_body)
+ {
+ }
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Block const& body() const { return *m_body; }
+
+ virtual TypePointer type() const override;
+
+ virtual ModifierDefinitionAnnotation& annotation() const override;
+
+private:
+ ASTPointer<Block> m_body;
+};
+
+/**
+ * Invocation/usage of a modifier in a function header or a base constructor call.
+ */
+class ModifierInvocation: public ASTNode
+{
+public:
+ ModifierInvocation(
+ SourceLocation const& _location,
+ ASTPointer<Identifier> const& _name,
+ std::vector<ASTPointer<Expression>> _arguments
+ ):
+ ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ASTPointer<Identifier> const& name() const { return m_modifierName; }
+ std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
+
+private:
+ ASTPointer<Identifier> m_modifierName;
+ std::vector<ASTPointer<Expression>> m_arguments;
+};
+
+/**
+ * Definition of a (loggable) event.
+ */
+class EventDefinition: public CallableDeclaration, public Documented
+{
+public:
+ EventDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ ASTPointer<ASTString> const& _documentation,
+ ASTPointer<ParameterList> const& _parameters,
+ bool _anonymous = false
+ ):
+ CallableDeclaration(_location, _name, Visibility::Default, _parameters),
+ Documented(_documentation),
+ m_anonymous(_anonymous)
+ {
+ }
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ bool isAnonymous() const { return m_anonymous; }
+
+ virtual TypePointer type() const override;
+
+ virtual EventDefinitionAnnotation& annotation() const override;
+
+private:
+ bool m_anonymous = false;
+};
+
+/**
+ * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
+ * functions when such an identifier is encountered. Will never have a valid location in the source code.
+ */
+class MagicVariableDeclaration: public Declaration
+{
+public:
+ MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type):
+ Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {}
+ virtual void accept(ASTVisitor&) override
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST."));
+ }
+ virtual void accept(ASTConstVisitor&) const override
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST."));
+ }
+
+ virtual TypePointer type() const override { return m_type; }
+
+private:
+ std::shared_ptr<Type const> m_type;
+};
+
+/// Types
+/// @{
+
+/**
+ * Abstract base class of a type name, can be any built-in or user-defined type.
+ */
+class TypeName: public ASTNode
+{
+public:
+ explicit TypeName(SourceLocation const& _location): ASTNode(_location) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ virtual TypeNameAnnotation& annotation() const override;
+};
+
+/**
+ * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings,
+ * contracts, functions, etc.
+ */
+class ElementaryTypeName: public TypeName
+{
+public:
+ ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem):
+ TypeName(_location), m_type(_elem)
+ {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ElementaryTypeNameToken const& typeName() const { return m_type; }
+
+private:
+ ElementaryTypeNameToken m_type;
+};
+
+/**
+ * Name referring to a user-defined type (i.e. a struct, contract, etc.).
+ */
+class UserDefinedTypeName: public TypeName
+{
+public:
+ UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath):
+ TypeName(_location), m_namePath(_namePath) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTString> const& namePath() const { return m_namePath; }
+
+ virtual UserDefinedTypeNameAnnotation& annotation() const override;
+
+private:
+ std::vector<ASTString> m_namePath;
+};
+
+/**
+ * A mapping type. Its source form is "mapping('keyType' => 'valueType')"
+ */
+class Mapping: public TypeName
+{
+public:
+ Mapping(
+ SourceLocation const& _location,
+ ASTPointer<ElementaryTypeName> const& _keyType,
+ ASTPointer<TypeName> const& _valueType
+ ):
+ TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ElementaryTypeName const& keyType() const { return *m_keyType; }
+ TypeName const& valueType() const { return *m_valueType; }
+
+private:
+ ASTPointer<ElementaryTypeName> m_keyType;
+ ASTPointer<TypeName> m_valueType;
+};
+
+/**
+ * An array type, can be "typename[]" or "typename[<expression>]".
+ */
+class ArrayTypeName: public TypeName
+{
+public:
+ ArrayTypeName(
+ SourceLocation const& _location,
+ ASTPointer<TypeName> const& _baseType,
+ ASTPointer<Expression> const& _length
+ ):
+ TypeName(_location), m_baseType(_baseType), m_length(_length) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ TypeName const& baseType() const { return *m_baseType; }
+ Expression const* length() const { return m_length.get(); }
+
+private:
+ ASTPointer<TypeName> m_baseType;
+ ASTPointer<Expression> m_length; ///< Length of the array, might be empty.
+};
+
+/// @}
+
+/// Statements
+/// @{
+
+
+/**
+ * Abstract base class for statements.
+ */
+class Statement: public ASTNode, public Documented
+{
+public:
+ explicit Statement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString
+ ): ASTNode(_location), Documented(_docString) {}
+
+ virtual StatementAnnotation& annotation() const override;
+};
+
+/**
+ * Brace-enclosed block containing zero or more statements.
+ */
+class Block: public Statement
+{
+public:
+ Block(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ std::vector<ASTPointer<Statement>> const& _statements
+ ):
+ Statement(_location, _docString), m_statements(_statements) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<Statement>> const& statements() const { return m_statements; }
+
+private:
+ std::vector<ASTPointer<Statement>> m_statements;
+};
+
+/**
+ * Special placeholder statement denoted by "_" used in function modifiers. This is replaced by
+ * the original function when the modifier is applied.
+ */
+class PlaceholderStatement: public Statement
+{
+public:
+ explicit PlaceholderStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString
+ ): Statement(_location, _docString) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+};
+
+/**
+ * If-statement with an optional "else" part. Note that "else if" is modeled by having a new
+ * if-statement as the false (else) body.
+ */
+class IfStatement: public Statement
+{
+public:
+ IfStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> const& _condition,
+ ASTPointer<Statement> const& _trueBody,
+ ASTPointer<Statement> const& _falseBody
+ ):
+ Statement(_location, _docString),
+ m_condition(_condition),
+ m_trueBody(_trueBody),
+ m_falseBody(_falseBody)
+ {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& condition() const { return *m_condition; }
+ Statement const& trueStatement() const { return *m_trueBody; }
+ /// @returns the "else" part of the if statement or nullptr if there is no "else" part.
+ Statement const* falseStatement() const { return m_falseBody.get(); }
+
+private:
+ ASTPointer<Expression> m_condition;
+ ASTPointer<Statement> m_trueBody;
+ ASTPointer<Statement> m_falseBody; ///< "else" part, optional
+};
+
+/**
+ * Statement in which a break statement is legal (abstract class).
+ */
+class BreakableStatement: public Statement
+{
+public:
+ explicit BreakableStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString
+ ): Statement(_location, _docString) {}
+};
+
+class WhileStatement: public BreakableStatement
+{
+public:
+ WhileStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> const& _condition,
+ ASTPointer<Statement> const& _body
+ ):
+ BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& condition() const { return *m_condition; }
+ Statement const& body() const { return *m_body; }
+
+private:
+ ASTPointer<Expression> m_condition;
+ ASTPointer<Statement> m_body;
+};
+
+/**
+ * For loop statement
+ */
+class ForStatement: public BreakableStatement
+{
+public:
+ ForStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Statement> const& _initExpression,
+ ASTPointer<Expression> const& _conditionExpression,
+ ASTPointer<ExpressionStatement> const& _loopExpression,
+ ASTPointer<Statement> const& _body
+ ):
+ BreakableStatement(_location, _docString),
+ m_initExpression(_initExpression),
+ m_condExpression(_conditionExpression),
+ m_loopExpression(_loopExpression),
+ m_body(_body)
+ {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Statement const* initializationExpression() const { return m_initExpression.get(); }
+ Expression const* condition() const { return m_condExpression.get(); }
+ ExpressionStatement const* loopExpression() const { return m_loopExpression.get(); }
+ Statement const& body() const { return *m_body; }
+
+private:
+ /// For statement's initialization expresion. for(XXX; ; ). Can be empty
+ ASTPointer<Statement> m_initExpression;
+ /// For statement's condition expresion. for(; XXX ; ). Can be empty
+ ASTPointer<Expression> m_condExpression;
+ /// For statement's loop expresion. for(;;XXX). Can be empty
+ ASTPointer<ExpressionStatement> m_loopExpression;
+ /// The body of the loop
+ ASTPointer<Statement> m_body;
+};
+
+class Continue: public Statement
+{
+public:
+ explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
+ Statement(_location, _docString) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+};
+
+class Break: public Statement
+{
+public:
+ explicit Break(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
+ Statement(_location, _docString) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+};
+
+class Return: public Statement
+{
+public:
+ Return(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> _expression
+ ): Statement(_location, _docString), m_expression(_expression) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const* expression() const { return m_expression.get(); }
+
+ virtual ReturnAnnotation& annotation() const override;
+
+private:
+ ASTPointer<Expression> m_expression; ///< value to return, optional
+};
+
+/**
+ * @brief The Throw statement to throw that triggers a solidity exception(jump to ErrorTag)
+ */
+class Throw: public Statement
+{
+public:
+ explicit Throw(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
+ Statement(_location, _docString) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+};
+
+/**
+ * Definition of a variable as a statement inside a function. It requires a type name (which can
+ * also be "var") but the actual assignment can be missing.
+ * Examples: var a = 2; uint256 a;
+ * As a second form, multiple variables can be declared, cannot have a type and must be assigned
+ * right away. If the first or last component is unnamed, it can "consume" an arbitrary number
+ * of components.
+ * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d();
+ */
+class VariableDeclarationStatement: public Statement
+{
+public:
+ VariableDeclarationStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ std::vector<ASTPointer<VariableDeclaration>> const& _variables,
+ ASTPointer<Expression> const& _initialValue
+ ):
+ Statement(_location, _docString), m_variables(_variables), m_initialValue(_initialValue) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ VariableDeclarationStatementAnnotation& annotation() const override;
+
+ std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
+ Expression const* initialValue() const { return m_initialValue.get(); }
+
+private:
+ /// List of variables, some of which can be empty pointers (unnamed components).
+ std::vector<ASTPointer<VariableDeclaration>> m_variables;
+ /// The assigned expression / initial value.
+ ASTPointer<Expression> m_initialValue;
+};
+
+/**
+ * A statement that contains only an expression (i.e. an assignment, function call, ...).
+ */
+class ExpressionStatement: public Statement
+{
+public:
+ ExpressionStatement(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> _expression
+ ):
+ Statement(_location, _docString), m_expression(_expression) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& expression() const { return *m_expression; }
+
+private:
+ ASTPointer<Expression> m_expression;
+};
+
+/// @}
+
+/// Expressions
+/// @{
+
+/**
+ * An expression, i.e. something that has a value (which can also be of type "void" in case
+ * of some function calls).
+ * @abstract
+ */
+class Expression: public ASTNode
+{
+public:
+ explicit Expression(SourceLocation const& _location): ASTNode(_location) {}
+
+ ExpressionAnnotation& annotation() const override;
+};
+
+class Conditional: public Expression
+{
+public:
+ Conditional(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _condition,
+ ASTPointer<Expression> const& _trueExpression,
+ ASTPointer<Expression> const& _falseExpression
+ ):
+ Expression(_location),
+ m_condition(_condition),
+ m_trueExpression(_trueExpression),
+ m_falseExpression(_falseExpression)
+ {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& condition() const { return *m_condition; }
+ Expression const& trueExpression() const { return *m_trueExpression; }
+ Expression const& falseExpression() const { return *m_falseExpression; }
+
+private:
+ ASTPointer<Expression> m_condition;
+ ASTPointer<Expression> m_trueExpression;
+ ASTPointer<Expression> m_falseExpression;
+};
+
+/// Assignment, can also be a compound assignment.
+/// Examples: (a = 7 + 8) or (a *= 2)
+class Assignment: public Expression
+{
+public:
+ Assignment(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _leftHandSide,
+ Token::Value _assignmentOperator,
+ ASTPointer<Expression> const& _rightHandSide
+ ):
+ Expression(_location),
+ m_leftHandSide(_leftHandSide),
+ m_assigmentOperator(_assignmentOperator),
+ m_rightHandSide(_rightHandSide)
+ {
+ solAssert(Token::isAssignmentOp(_assignmentOperator), "");
+ }
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& leftHandSide() const { return *m_leftHandSide; }
+ Token::Value assignmentOperator() const { return m_assigmentOperator; }
+ Expression const& rightHandSide() const { return *m_rightHandSide; }
+
+private:
+ ASTPointer<Expression> m_leftHandSide;
+ Token::Value m_assigmentOperator;
+ ASTPointer<Expression> m_rightHandSide;
+};
+
+
+/**
+ * Tuple, parenthesized expression, or bracketed expression.
+ * Examples: (1, 2), (x,), (x), (), [1, 2],
+ * Individual components might be empty shared pointers (as in the second example).
+ * The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple
+ * Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple.
+ */
+class TupleExpression: public Expression
+{
+public:
+ TupleExpression(
+ SourceLocation const& _location,
+ std::vector<ASTPointer<Expression>> const& _components,
+ bool _isArray
+ ):
+ Expression(_location),
+ m_components(_components),
+ m_isArray(_isArray) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ std::vector<ASTPointer<Expression>> const& components() const { return m_components; }
+ bool isInlineArray() const { return m_isArray; }
+
+private:
+ std::vector<ASTPointer<Expression>> m_components;
+ bool m_isArray;
+};
+
+/**
+ * Operation involving a unary operator, pre- or postfix.
+ * Examples: ++i, delete x or !true
+ */
+class UnaryOperation: public Expression
+{
+public:
+ UnaryOperation(
+ SourceLocation const& _location,
+ Token::Value _operator,
+ ASTPointer<Expression> const& _subExpression,
+ bool _isPrefix
+ ):
+ Expression(_location),
+ m_operator(_operator),
+ m_subExpression(_subExpression),
+ m_isPrefix(_isPrefix)
+ {
+ solAssert(Token::isUnaryOp(_operator), "");
+ }
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Token::Value getOperator() const { return m_operator; }
+ bool isPrefixOperation() const { return m_isPrefix; }
+ Expression const& subExpression() const { return *m_subExpression; }
+
+private:
+ Token::Value m_operator;
+ ASTPointer<Expression> m_subExpression;
+ bool m_isPrefix;
+};
+
+/**
+ * Operation involving a binary operator.
+ * Examples: 1 + 2, true && false or 1 <= 4
+ */
+class BinaryOperation: public Expression
+{
+public:
+ BinaryOperation(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _left,
+ Token::Value _operator,
+ ASTPointer<Expression> const& _right
+ ):
+ Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
+ {
+ solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), "");
+ }
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& leftExpression() const { return *m_left; }
+ Expression const& rightExpression() const { return *m_right; }
+ Token::Value getOperator() const { return m_operator; }
+
+ BinaryOperationAnnotation& annotation() const override;
+
+private:
+ ASTPointer<Expression> m_left;
+ Token::Value m_operator;
+ ASTPointer<Expression> m_right;
+};
+
+/**
+ * Can be ordinary function call, type cast or struct construction.
+ */
+class FunctionCall: public Expression
+{
+public:
+ FunctionCall(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _expression,
+ std::vector<ASTPointer<Expression>> const& _arguments,
+ std::vector<ASTPointer<ASTString>> const& _names
+ ):
+ Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& expression() const { return *m_expression; }
+ std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
+ std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
+
+ virtual FunctionCallAnnotation& annotation() const override;
+
+private:
+ ASTPointer<Expression> m_expression;
+ std::vector<ASTPointer<Expression>> m_arguments;
+ std::vector<ASTPointer<ASTString>> m_names;
+};
+
+/**
+ * Expression that creates a new contract or memory-array,
+ * e.g. the "new SomeContract" part in "new SomeContract(1, 2)".
+ */
+class NewExpression: public Expression
+{
+public:
+ NewExpression(
+ SourceLocation const& _location,
+ ASTPointer<TypeName> const& _typeName
+ ):
+ Expression(_location), m_typeName(_typeName) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ TypeName const& typeName() const { return *m_typeName; }
+
+private:
+ ASTPointer<TypeName> m_typeName;
+};
+
+/**
+ * Access to a member of an object. Example: x.name
+ */
+class MemberAccess: public Expression
+{
+public:
+ MemberAccess(
+ SourceLocation const& _location,
+ ASTPointer<Expression> _expression,
+ ASTPointer<ASTString> const& _memberName
+ ):
+ Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+ Expression const& expression() const { return *m_expression; }
+ ASTString const& memberName() const { return *m_memberName; }
+
+ virtual MemberAccessAnnotation& annotation() const override;
+
+private:
+ ASTPointer<Expression> m_expression;
+ ASTPointer<ASTString> m_memberName;
+};
+
+/**
+ * Index access to an array. Example: a[2]
+ */
+class IndexAccess: public Expression
+{
+public:
+ IndexAccess(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _base,
+ ASTPointer<Expression> const& _index
+ ):
+ Expression(_location), m_base(_base), m_index(_index) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Expression const& baseExpression() const { return *m_base; }
+ Expression const* indexExpression() const { return m_index.get(); }
+
+private:
+ ASTPointer<Expression> m_base;
+ ASTPointer<Expression> m_index;
+};
+
+/**
+ * Primary expression, i.e. an expression that cannot be divided any further. Examples are literals
+ * or variable references.
+ */
+class PrimaryExpression: public Expression
+{
+public:
+ PrimaryExpression(SourceLocation const& _location): Expression(_location) {}
+};
+
+/**
+ * An identifier, i.e. a reference to a declaration by name like a variable or function.
+ */
+class Identifier: public PrimaryExpression
+{
+public:
+ Identifier(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name
+ ):
+ PrimaryExpression(_location), m_name(_name) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ASTString const& name() const { return *m_name; }
+
+ virtual IdentifierAnnotation& annotation() const override;
+
+private:
+ ASTPointer<ASTString> m_name;
+};
+
+/**
+ * An elementary type name expression is used in expressions like "a = uint32(2)" to change the
+ * type of an expression explicitly. Here, "uint32" is the elementary type name expression and
+ * "uint32(2)" is a @ref FunctionCall.
+ */
+class ElementaryTypeNameExpression: public PrimaryExpression
+{
+public:
+ ElementaryTypeNameExpression(SourceLocation const& _location, ElementaryTypeNameToken const& _type):
+ PrimaryExpression(_location), m_typeToken(_type)
+ {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ElementaryTypeNameToken const& typeName() const { return m_typeToken; }
+
+private:
+ ElementaryTypeNameToken m_typeToken;
+};
+
+/**
+ * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value.
+ */
+class Literal: public PrimaryExpression
+{
+public:
+ enum class SubDenomination
+ {
+ None = Token::Illegal,
+ Wei = Token::SubWei,
+ Szabo = Token::SubSzabo,
+ Finney = Token::SubFinney,
+ Ether = Token::SubEther,
+ Second = Token::SubSecond,
+ Minute = Token::SubMinute,
+ Hour = Token::SubHour,
+ Day = Token::SubDay,
+ Week = Token::SubWeek,
+ Year = Token::SubYear
+ };
+ Literal(
+ SourceLocation const& _location,
+ Token::Value _token,
+ ASTPointer<ASTString> const& _value,
+ SubDenomination _sub = SubDenomination::None
+ ):
+ PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Token::Value token() const { return m_token; }
+ /// @returns the non-parsed value of the literal
+ ASTString const& value() const { return *m_value; }
+
+ SubDenomination subDenomination() const { return m_subDenomination; }
+
+private:
+ Token::Value m_token;
+ ASTPointer<ASTString> m_value;
+ SubDenomination m_subDenomination;
+};
+
+/// @}
+
+
+}
+}
diff --git a/libsolidity/ast/ASTAnnotations.cpp b/libsolidity/ast/ASTAnnotations.cpp
new file mode 100644
index 00000000..416e6b44
--- /dev/null
+++ b/libsolidity/ast/ASTAnnotations.cpp
@@ -0,0 +1,28 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Object containing the type and other annotations for the AST nodes.
+ */
+
+#include <libsolidity/ast/ASTAnnotations.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
new file mode 100644
index 00000000..235338bb
--- /dev/null
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -0,0 +1,185 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Object containing the type and other annotations for the AST nodes.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+#include <set>
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Type;
+using TypePointer = std::shared_ptr<Type const>;
+
+struct ASTAnnotation
+{
+ virtual ~ASTAnnotation() {}
+};
+
+struct DocTag
+{
+ std::string content; ///< The text content of the tag.
+ std::string paramName; ///< Only used for @param, stores the parameter name.
+};
+
+struct DocumentedAnnotation
+{
+ virtual ~DocumentedAnnotation() {}
+ /// Mapping docstring tag name -> content.
+ std::multimap<std::string, DocTag> docTags;
+};
+
+struct SourceUnitAnnotation: ASTAnnotation
+{
+ /// The "absolute" (in the compiler sense) path of this source unit.
+ std::string path;
+ /// The exported symbols (all global symbols).
+ std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
+};
+
+struct ImportAnnotation: ASTAnnotation
+{
+ /// The absolute path of the source unit to import.
+ std::string absolutePath;
+ /// The actual source unit.
+ SourceUnit const* sourceUnit = nullptr;
+};
+
+struct TypeDeclarationAnnotation: ASTAnnotation
+{
+ /// The name of this type, prefixed by proper namespaces if globally accessible.
+ std::string canonicalName;
+};
+
+struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation
+{
+ /// Whether all functions are implemented.
+ bool isFullyImplemented = true;
+ /// List of all (direct and indirect) base contracts in order from derived to
+ /// base, including the contract itself.
+ std::vector<ContractDefinition const*> linearizedBaseContracts;
+ /// List of contracts this contract creates, i.e. which need to be compiled first.
+ /// Also includes all contracts from @a linearizedBaseContracts.
+ std::set<ContractDefinition const*> contractDependencies;
+};
+
+struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
+{
+};
+
+struct EventDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
+{
+};
+
+struct ModifierDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
+{
+};
+
+struct VariableDeclarationAnnotation: ASTAnnotation
+{
+ /// Type of variable (type of identifier referencing this variable).
+ TypePointer type;
+};
+
+struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation
+{
+};
+
+struct ReturnAnnotation: StatementAnnotation
+{
+ /// Reference to the return parameters of the function.
+ ParameterList const* functionReturnParameters = nullptr;
+};
+
+struct TypeNameAnnotation: ASTAnnotation
+{
+ /// Type declared by this type name, i.e. type of a variable where this type name is used.
+ /// Set during reference resolution stage.
+ TypePointer type;
+};
+
+struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
+{
+ /// Referenced declaration, set during reference resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+ /// Stores a reference to the current contract.
+ /// This is needed because types of base contracts change depending on the context.
+ ContractDefinition const* contractScope = nullptr;
+};
+
+struct VariableDeclarationStatementAnnotation: StatementAnnotation
+{
+ /// Information about which component of the value is assigned to which variable.
+ /// The pointer can be null to signify that the component is discarded.
+ std::vector<VariableDeclaration const*> assignments;
+};
+
+struct ExpressionAnnotation: ASTAnnotation
+{
+ /// Inferred type of the expression.
+ TypePointer type;
+ /// Whether it is an LValue (i.e. something that can be assigned to).
+ bool isLValue = false;
+ /// Whether the expression is used in a context where the LValue is actually required.
+ bool lValueRequested = false;
+ /// Types of arguments if the expression is a function that is called - used
+ /// for overload resolution.
+ std::shared_ptr<std::vector<TypePointer>> argumentTypes;
+};
+
+struct IdentifierAnnotation: ExpressionAnnotation
+{
+ /// Referenced declaration, set at latest during overload resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+ /// List of possible declarations it could refer to.
+ std::vector<Declaration const*> overloadedDeclarations;
+};
+
+struct MemberAccessAnnotation: ExpressionAnnotation
+{
+ /// Referenced declaration, set at latest during overload resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+};
+
+struct BinaryOperationAnnotation: ExpressionAnnotation
+{
+ /// The common type that is used for the operation, not necessarily the result type (which
+ /// e.g. for comparisons is bool).
+ TypePointer commonType;
+};
+
+struct FunctionCallAnnotation: ExpressionAnnotation
+{
+ /// Whether this is an explicit type conversion.
+ bool isTypeConversion = false;
+ /// Whether this is a struct constructor call.
+ bool isStructConstructorCall = false;
+};
+
+}
+}
diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h
new file mode 100644
index 00000000..dad2b2e2
--- /dev/null
+++ b/libsolidity/ast/ASTForward.h
@@ -0,0 +1,98 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Forward-declarations of AST classes.
+ */
+
+#pragma once
+
+#include <string>
+#include <memory>
+#include <vector>
+
+// Forward-declare all AST node types
+
+namespace dev
+{
+namespace solidity
+{
+
+class ASTNode;
+class SourceUnit;
+class ImportDirective;
+class Declaration;
+class ContractDefinition;
+class InheritanceSpecifier;
+class UsingForDirective;
+class StructDefinition;
+class EnumDefinition;
+class EnumValue;
+class ParameterList;
+class FunctionDefinition;
+class VariableDeclaration;
+class ModifierDefinition;
+class ModifierInvocation;
+class EventDefinition;
+class MagicVariableDeclaration;
+class TypeName;
+class ElementaryTypeName;
+class UserDefinedTypeName;
+class Mapping;
+class ArrayTypeName;
+class Statement;
+class Block;
+class PlaceholderStatement;
+class IfStatement;
+class BreakableStatement;
+class WhileStatement;
+class ForStatement;
+class Continue;
+class Break;
+class Return;
+class Throw;
+class VariableDeclarationStatement;
+class ExpressionStatement;
+class Expression;
+class Conditional;
+class Assignment;
+class TupleExpression;
+class UnaryOperation;
+class BinaryOperation;
+class FunctionCall;
+class NewExpression;
+class MemberAccess;
+class IndexAccess;
+class PrimaryExpression;
+class Identifier;
+class ElementaryTypeNameExpression;
+class Literal;
+
+class VariableScope;
+
+// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
+// not do reference counting but point to a special memory area that is completely released
+// explicitly.
+template <class T>
+using ASTPointer = std::shared_ptr<T>;
+
+using ASTString = std::string;
+
+
+}
+}
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
new file mode 100644
index 00000000..163e22f4
--- /dev/null
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -0,0 +1,481 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2015
+ * Converts the AST into json format
+ */
+
+#include <libsolidity/ast/ASTJsonConverter.h>
+#include <boost/algorithm/string/join.hpp>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val)
+{
+ // special handling for booleans
+ if (_key == "const" || _key == "public" || _key == "local" ||
+ _key == "lvalue" || _key == "local_lvalue" || _key == "prefix")
+ _obj[_key] = (_val == "1") ? true : false;
+ else
+ // else simply add it as a string
+ _obj[_key] = _val;
+}
+
+void ASTJsonConverter::addJsonNode(string const& _nodeName,
+ initializer_list<pair<string const, string const>> _list,
+ bool _hasChildren = false)
+{
+ Json::Value node;
+
+ node["name"] = _nodeName;
+ if (_list.size() != 0)
+ {
+ Json::Value attrs;
+ for (auto& e: _list)
+ addKeyValue(attrs, e.first, e.second);
+ node["attributes"] = attrs;
+ }
+
+ m_jsonNodePtrs.top()->append(node);
+
+ if (_hasChildren)
+ {
+ Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
+ Json::Value children(Json::arrayValue);
+ addedNode["children"] = children;
+ m_jsonNodePtrs.push(&addedNode["children"]);
+ }
+}
+
+ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast)
+{
+ Json::Value children(Json::arrayValue);
+
+ m_astJson["name"] = "root";
+ m_astJson["children"] = children;
+ m_jsonNodePtrs.push(&m_astJson["children"]);
+}
+
+void ASTJsonConverter::print(ostream& _stream)
+{
+ process();
+ _stream << m_astJson;
+}
+
+Json::Value const& ASTJsonConverter::json()
+{
+ process();
+ return m_astJson;
+}
+
+bool ASTJsonConverter::visit(ImportDirective const& _node)
+{
+ addJsonNode("Import", { make_pair("file", _node.path())});
+ return true;
+}
+
+bool ASTJsonConverter::visit(ContractDefinition const& _node)
+{
+ addJsonNode("Contract", { make_pair("name", _node.name()) }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(StructDefinition const& _node)
+{
+ addJsonNode("Struct", { make_pair("name", _node.name()) }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(ParameterList const&)
+{
+ addJsonNode("ParameterList", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(FunctionDefinition const& _node)
+{
+ addJsonNode("Function",
+ { make_pair("name", _node.name()),
+ make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())),
+ make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) },
+ true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(VariableDeclaration const& _node)
+{
+ addJsonNode("VariableDeclaration", {
+ make_pair("name", _node.name()),
+ make_pair("name", _node.name()),
+ }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(TypeName const&)
+{
+ return true;
+}
+
+bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
+{
+ addJsonNode("ElementaryTypeName", { make_pair("name", _node.typeName().toString()) });
+ return true;
+}
+
+bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
+{
+ addJsonNode("UserDefinedTypeName", {
+ make_pair("name", boost::algorithm::join(_node.namePath(), "."))
+ });
+ return true;
+}
+
+bool ASTJsonConverter::visit(Mapping const&)
+{
+ addJsonNode("Mapping", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(Block const&)
+{
+ addJsonNode("Block", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(IfStatement const&)
+{
+ addJsonNode("IfStatement", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(WhileStatement const&)
+{
+ addJsonNode("WhileStatement", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(ForStatement const&)
+{
+ addJsonNode("ForStatement", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(Continue const&)
+{
+ addJsonNode("Continue", {});
+ return true;
+}
+
+bool ASTJsonConverter::visit(Break const&)
+{
+ addJsonNode("Break", {});
+ return true;
+}
+
+bool ASTJsonConverter::visit(Return const&)
+{
+ addJsonNode("Return", {}, true);;
+ return true;
+}
+
+bool ASTJsonConverter::visit(Throw const&)
+{
+ addJsonNode("Throw", {}, true);;
+ return true;
+}
+
+bool ASTJsonConverter::visit(VariableDeclarationStatement const&)
+{
+ addJsonNode("VariableDefinition", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(ExpressionStatement const&)
+{
+ addJsonNode("ExpressionStatement", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(Conditional const&)
+{
+ addJsonNode("Conditional", {}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(Assignment const& _node)
+{
+ addJsonNode("Assignment",
+ { make_pair("operator", Token::toString(_node.assignmentOperator())),
+ make_pair("type", type(_node)) },
+ true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(TupleExpression const&)
+{
+ addJsonNode("TupleExpression",{}, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(UnaryOperation const& _node)
+{
+ addJsonNode("UnaryOperation",
+ { make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())),
+ make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("type", type(_node)) },
+ true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(BinaryOperation const& _node)
+{
+ addJsonNode("BinaryOperation", {
+ make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("type", type(_node))
+ }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(FunctionCall const& _node)
+{
+ addJsonNode("FunctionCall", {
+ make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)),
+ make_pair("type", type(_node))
+ }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(NewExpression const& _node)
+{
+ addJsonNode("NewExpression", { make_pair("type", type(_node)) }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(MemberAccess const& _node)
+{
+ addJsonNode("MemberAccess",
+ { make_pair("member_name", _node.memberName()),
+ make_pair("type", type(_node)) },
+ true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(IndexAccess const& _node)
+{
+ addJsonNode("IndexAccess", { make_pair("type", type(_node)) }, true);
+ return true;
+}
+
+bool ASTJsonConverter::visit(Identifier const& _node)
+{
+ addJsonNode("Identifier",
+ { make_pair("value", _node.name()), make_pair("type", type(_node)) });
+ return true;
+}
+
+bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
+{
+ addJsonNode("ElementaryTypenameExpression",
+ { make_pair("value", _node.typeName().toString()), make_pair("type", type(_node)) });
+ return true;
+}
+
+bool ASTJsonConverter::visit(Literal const& _node)
+{
+ char const* tokenString = Token::toString(_node.token());
+ addJsonNode("Literal",
+ { make_pair("string", (tokenString) ? tokenString : "null"),
+ make_pair("value", _node.value()),
+ make_pair("type", type(_node)) });
+ return true;
+}
+
+void ASTJsonConverter::endVisit(ImportDirective const&)
+{
+}
+
+void ASTJsonConverter::endVisit(ContractDefinition const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(StructDefinition const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(ParameterList const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(FunctionDefinition const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(VariableDeclaration const&)
+{
+}
+
+void ASTJsonConverter::endVisit(TypeName const&)
+{
+}
+
+void ASTJsonConverter::endVisit(ElementaryTypeName const&)
+{
+}
+
+void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
+{
+}
+
+void ASTJsonConverter::endVisit(Mapping const&)
+{
+}
+
+void ASTJsonConverter::endVisit(Block const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(IfStatement const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(WhileStatement const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(ForStatement const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(Continue const&)
+{
+}
+
+void ASTJsonConverter::endVisit(Break const&)
+{
+}
+
+void ASTJsonConverter::endVisit(Return const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(Throw const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(VariableDeclarationStatement const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(ExpressionStatement const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(Conditional const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(Assignment const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(TupleExpression const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(UnaryOperation const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(BinaryOperation const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(FunctionCall const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(NewExpression const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(MemberAccess const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(IndexAccess const&)
+{
+ goUp();
+}
+
+void ASTJsonConverter::endVisit(Identifier const&)
+{
+}
+
+void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
+{
+}
+
+void ASTJsonConverter::endVisit(Literal const&)
+{
+}
+
+void ASTJsonConverter::process()
+{
+ if (!processed)
+ m_ast->accept(*this);
+ processed = true;
+}
+
+string ASTJsonConverter::type(Expression const& _expression)
+{
+ return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
+}
+
+string ASTJsonConverter::type(VariableDeclaration const& _varDecl)
+{
+ return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown";
+}
+
+}
+}
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
new file mode 100644
index 00000000..b7fc84e9
--- /dev/null
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -0,0 +1,138 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2015
+ * Converts the AST into json format
+ */
+
+#pragma once
+
+#include <ostream>
+#include <stack>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+#include <json/json.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Converter of the AST into JSON format
+ */
+class ASTJsonConverter: public ASTConstVisitor
+{
+public:
+ /// Create a converter to JSON for the given abstract syntax tree.
+ explicit ASTJsonConverter(ASTNode const& _ast);
+ /// Output the json representation of the AST to _stream.
+ void print(std::ostream& _stream);
+ Json::Value const& json();
+
+ bool visit(ImportDirective const& _node) override;
+ bool visit(ContractDefinition const& _node) override;
+ bool visit(StructDefinition const& _node) override;
+ bool visit(ParameterList const& _node) override;
+ bool visit(FunctionDefinition const& _node) override;
+ bool visit(VariableDeclaration const& _node) override;
+ bool visit(TypeName const& _node) override;
+ bool visit(ElementaryTypeName const& _node) override;
+ bool visit(UserDefinedTypeName const& _node) override;
+ bool visit(Mapping const& _node) override;
+ bool visit(Block const& _node) override;
+ bool visit(IfStatement const& _node) override;
+ bool visit(WhileStatement const& _node) override;
+ bool visit(ForStatement const& _node) override;
+ bool visit(Continue const& _node) override;
+ bool visit(Break const& _node) override;
+ bool visit(Return const& _node) override;
+ bool visit(Throw const& _node) override;
+ bool visit(VariableDeclarationStatement const& _node) override;
+ bool visit(ExpressionStatement const& _node) override;
+ bool visit(Conditional const& _node) override;
+ bool visit(Assignment const& _node) override;
+ bool visit(TupleExpression const& _node) override;
+ bool visit(UnaryOperation const& _node) override;
+ bool visit(BinaryOperation const& _node) override;
+ bool visit(FunctionCall const& _node) override;
+ bool visit(NewExpression const& _node) override;
+ bool visit(MemberAccess const& _node) override;
+ bool visit(IndexAccess const& _node) override;
+ bool visit(Identifier const& _node) override;
+ bool visit(ElementaryTypeNameExpression const& _node) override;
+ bool visit(Literal const& _node) override;
+
+ void endVisit(ImportDirective const&) override;
+ void endVisit(ContractDefinition const&) override;
+ void endVisit(StructDefinition const&) override;
+ void endVisit(ParameterList const&) override;
+ void endVisit(FunctionDefinition const&) override;
+ void endVisit(VariableDeclaration const&) override;
+ void endVisit(TypeName const&) override;
+ void endVisit(ElementaryTypeName const&) override;
+ void endVisit(UserDefinedTypeName const&) override;
+ void endVisit(Mapping const&) override;
+ void endVisit(Block const&) override;
+ void endVisit(IfStatement const&) override;
+ void endVisit(WhileStatement const&) override;
+ void endVisit(ForStatement const&) override;
+ void endVisit(Continue const&) override;
+ void endVisit(Break const&) override;
+ void endVisit(Return const&) override;
+ void endVisit(Throw const&) override;
+ void endVisit(VariableDeclarationStatement const&) override;
+ void endVisit(ExpressionStatement const&) override;
+ void endVisit(Conditional const&) override;
+ void endVisit(Assignment const&) override;
+ void endVisit(TupleExpression const&) override;
+ void endVisit(UnaryOperation const&) override;
+ void endVisit(BinaryOperation const&) override;
+ void endVisit(FunctionCall const&) override;
+ void endVisit(NewExpression const&) override;
+ void endVisit(MemberAccess const&) override;
+ void endVisit(IndexAccess const&) override;
+ void endVisit(Identifier const&) override;
+ void endVisit(ElementaryTypeNameExpression const&) override;
+ void endVisit(Literal const&) override;
+
+private:
+ void process();
+ void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val);
+ void addJsonNode(std::string const& _nodeName,
+ std::initializer_list<std::pair<std::string const, std::string const>> _list,
+ bool _hasChildren);
+ std::string type(Expression const& _expression);
+ std::string type(VariableDeclaration const& _varDecl);
+ inline void goUp()
+ {
+ solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error.");
+ m_jsonNodePtrs.pop();
+ }
+
+ bool processed = false;
+ Json::Value m_astJson;
+ std::stack<Json::Value*> m_jsonNodePtrs;
+ std::string m_source;
+ ASTNode const* m_ast;
+};
+
+}
+}
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
new file mode 100644
index 00000000..283bc8f9
--- /dev/null
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -0,0 +1,582 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
+ */
+
+#include <libsolidity/ast/ASTPrinter.h>
+#include <boost/algorithm/string/join.hpp>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+ASTPrinter::ASTPrinter(
+ ASTNode const& _ast,
+ string const& _source,
+ GasEstimator::ASTGasConsumption const& _gasCosts
+): m_indentation(0), m_source(_source), m_ast(&_ast), m_gasCosts(_gasCosts)
+{
+}
+
+void ASTPrinter::print(ostream& _stream)
+{
+ m_ostream = &_stream;
+ m_ast->accept(*this);
+ m_ostream = nullptr;
+}
+
+
+bool ASTPrinter::visit(ImportDirective const& _node)
+{
+ writeLine("ImportDirective \"" + _node.path() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ContractDefinition const& _node)
+{
+ writeLine("ContractDefinition \"" + _node.name() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(InheritanceSpecifier const& _node)
+{
+ writeLine("InheritanceSpecifier");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(StructDefinition const& _node)
+{
+ writeLine("StructDefinition \"" + _node.name() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(EnumDefinition const& _node)
+{
+ writeLine("EnumDefinition \"" + _node.name() + "\"");
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(EnumValue const& _node)
+{
+ writeLine("EnumValue \"" + _node.name() + "\"");
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ParameterList const& _node)
+{
+ writeLine("ParameterList");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(FunctionDefinition const& _node)
+{
+ writeLine("FunctionDefinition \"" + _node.name() + "\"" +
+ (_node.isPublic() ? " - public" : "") +
+ (_node.isDeclaredConst() ? " - const" : ""));
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(VariableDeclaration const& _node)
+{
+ writeLine("VariableDeclaration \"" + _node.name() + "\"");
+ *m_ostream << indentation() << (
+ _node.annotation().type ?
+ string(" Type: ") + _node.annotation().type->toString() :
+ string(" Type unknown.")
+ ) << "\n";
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ModifierDefinition const& _node)
+{
+ writeLine("ModifierDefinition \"" + _node.name() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ModifierInvocation const& _node)
+{
+ writeLine("ModifierInvocation \"" + _node.name()->name() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(EventDefinition const& _node)
+{
+ writeLine("EventDefinition \"" + _node.name() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(TypeName const& _node)
+{
+ writeLine("TypeName");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ElementaryTypeName const& _node)
+{
+ writeLine(string("ElementaryTypeName ") + _node.typeName().toString());
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(UserDefinedTypeName const& _node)
+{
+ writeLine("UserDefinedTypeName \"" + boost::algorithm::join(_node.namePath(), ".") + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Mapping const& _node)
+{
+ writeLine("Mapping");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ArrayTypeName const& _node)
+{
+ writeLine("ArrayTypeName");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Block const& _node)
+{
+ writeLine("Block");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(PlaceholderStatement const& _node)
+{
+ writeLine("PlaceholderStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(IfStatement const& _node)
+{
+ writeLine("IfStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(WhileStatement const& _node)
+{
+ writeLine("WhileStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ForStatement const& _node)
+{
+ writeLine("ForStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Continue const& _node)
+{
+ writeLine("Continue");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Break const& _node)
+{
+ writeLine("Break");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Return const& _node)
+{
+ writeLine("Return");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Throw const& _node)
+{
+ writeLine("Throw");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(VariableDeclarationStatement const& _node)
+{
+ writeLine("VariableDeclarationStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ExpressionStatement const& _node)
+{
+ writeLine("ExpressionStatement");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Conditional const& _node)
+{
+ writeLine("Conditional");
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Assignment const& _node)
+{
+ writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(TupleExpression const& _node)
+{
+ writeLine(string("TupleExpression"));
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(UnaryOperation const& _node)
+{
+ writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
+ ") " + Token::toString(_node.getOperator()));
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(BinaryOperation const& _node)
+{
+ writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(FunctionCall const& _node)
+{
+ writeLine("FunctionCall");
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(NewExpression const& _node)
+{
+ writeLine("NewExpression");
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(MemberAccess const& _node)
+{
+ writeLine("MemberAccess to member " + _node.memberName());
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(IndexAccess const& _node)
+{
+ writeLine("IndexAccess");
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Identifier const& _node)
+{
+ writeLine(string("Identifier ") + _node.name());
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
+{
+ writeLine(string("ElementaryTypeNameExpression ") + _node.typeName().toString());
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+bool ASTPrinter::visit(Literal const& _node)
+{
+ char const* tokenString = Token::toString(_node.token());
+ if (!tokenString)
+ tokenString = "[no token]";
+ writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value());
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
+void ASTPrinter::endVisit(ImportDirective const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ContractDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(InheritanceSpecifier const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(StructDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(EnumDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(EnumValue const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ParameterList const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(FunctionDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(VariableDeclaration const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ModifierDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ModifierInvocation const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(EventDefinition const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(TypeName const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ElementaryTypeName const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(UserDefinedTypeName const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Mapping const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ArrayTypeName const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Block const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(PlaceholderStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(IfStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(WhileStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ForStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Continue const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Break const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Return const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Throw const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(VariableDeclarationStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ExpressionStatement const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Conditional const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Assignment const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(TupleExpression const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(UnaryOperation const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(BinaryOperation const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(FunctionCall const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(NewExpression const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(MemberAccess const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(IndexAccess const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Identifier const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(ElementaryTypeNameExpression const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::endVisit(Literal const&)
+{
+ m_indentation--;
+}
+
+void ASTPrinter::printSourcePart(ASTNode const& _node)
+{
+ if (m_gasCosts.count(&_node))
+ *m_ostream << indentation() << " Gas costs: " << m_gasCosts.at(&_node) << endl;
+ if (!m_source.empty())
+ {
+ SourceLocation const& location(_node.location());
+ *m_ostream << indentation() << " Source: "
+ << escaped(m_source.substr(location.start, location.end - location.start), false) << endl;
+ }
+}
+
+void ASTPrinter::printType(Expression const& _expression)
+{
+ if (_expression.annotation().type)
+ *m_ostream << indentation() << " Type: " << _expression.annotation().type->toString() << "\n";
+ else
+ *m_ostream << indentation() << " Type unknown.\n";
+}
+
+string ASTPrinter::indentation() const
+{
+ return string(m_indentation * 2, ' ');
+}
+
+void ASTPrinter::writeLine(string const& _line)
+{
+ *m_ostream << indentation() << _line << endl;
+}
+
+}
+}
diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h
new file mode 100644
index 00000000..334fefc7
--- /dev/null
+++ b/libsolidity/ast/ASTPrinter.h
@@ -0,0 +1,147 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/GasEstimator.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
+ */
+class ASTPrinter: public ASTConstVisitor
+{
+public:
+ /// Create a printer for the given abstract syntax tree. If the source is specified,
+ /// the corresponding parts of the source are printed with each node.
+ ASTPrinter(
+ ASTNode const& _ast,
+ std::string const& _source = std::string(),
+ GasEstimator::ASTGasConsumption const& _gasCosts = GasEstimator::ASTGasConsumption()
+ );
+ /// Output the string representation of the AST to _stream.
+ void print(std::ostream& _stream);
+
+ bool visit(ImportDirective const& _node) override;
+ bool visit(ContractDefinition const& _node) override;
+ bool visit(InheritanceSpecifier const& _node) override;
+ bool visit(StructDefinition const& _node) override;
+ bool visit(EnumDefinition const& _node) override;
+ bool visit(EnumValue const& _node) override;
+ bool visit(ParameterList const& _node) override;
+ bool visit(FunctionDefinition const& _node) override;
+ bool visit(VariableDeclaration const& _node) override;
+ bool visit(ModifierDefinition const& _node) override;
+ bool visit(ModifierInvocation const& _node) override;
+ bool visit(EventDefinition const& _node) override;
+ bool visit(TypeName const& _node) override;
+ bool visit(ElementaryTypeName const& _node) override;
+ bool visit(UserDefinedTypeName const& _node) override;
+ bool visit(Mapping const& _node) override;
+ bool visit(ArrayTypeName const& _node) override;
+ bool visit(Block const& _node) override;
+ bool visit(PlaceholderStatement const& _node) override;
+ bool visit(IfStatement const& _node) override;
+ bool visit(WhileStatement const& _node) override;
+ bool visit(ForStatement const& _node) override;
+ bool visit(Continue const& _node) override;
+ bool visit(Break const& _node) override;
+ bool visit(Return const& _node) override;
+ bool visit(Throw const& _node) override;
+ bool visit(VariableDeclarationStatement const& _node) override;
+ bool visit(ExpressionStatement const& _node) override;
+ bool visit(Conditional const& _node) override;
+ bool visit(Assignment const& _node) override;
+ bool visit(TupleExpression const& _node) override;
+ bool visit(UnaryOperation const& _node) override;
+ bool visit(BinaryOperation const& _node) override;
+ bool visit(FunctionCall const& _node) override;
+ bool visit(NewExpression const& _node) override;
+ bool visit(MemberAccess const& _node) override;
+ bool visit(IndexAccess const& _node) override;
+ bool visit(Identifier const& _node) override;
+ bool visit(ElementaryTypeNameExpression const& _node) override;
+ bool visit(Literal const& _node) override;
+
+ void endVisit(ImportDirective const&) override;
+ void endVisit(ContractDefinition const&) override;
+ void endVisit(InheritanceSpecifier const&) override;
+ void endVisit(StructDefinition const&) override;
+ void endVisit(EnumDefinition const&) override;
+ void endVisit(EnumValue const&) override;
+ void endVisit(ParameterList const&) override;
+ void endVisit(FunctionDefinition const&) override;
+ void endVisit(VariableDeclaration const&) override;
+ void endVisit(ModifierDefinition const&) override;
+ void endVisit(ModifierInvocation const&) override;
+ void endVisit(EventDefinition const&) override;
+ void endVisit(TypeName const&) override;
+ void endVisit(ElementaryTypeName const&) override;
+ void endVisit(UserDefinedTypeName const&) override;
+ void endVisit(Mapping const&) override;
+ void endVisit(ArrayTypeName const&) override;
+ void endVisit(Block const&) override;
+ void endVisit(PlaceholderStatement const&) override;
+ void endVisit(IfStatement const&) override;
+ void endVisit(WhileStatement const&) override;
+ void endVisit(ForStatement const&) override;
+ void endVisit(Continue const&) override;
+ void endVisit(Break const&) override;
+ void endVisit(Return const&) override;
+ void endVisit(Throw const&) override;
+ void endVisit(VariableDeclarationStatement const&) override;
+ void endVisit(ExpressionStatement const&) override;
+ void endVisit(Conditional const&) override;
+ void endVisit(Assignment const&) override;
+ void endVisit(TupleExpression const&) override;
+ void endVisit(UnaryOperation const&) override;
+ void endVisit(BinaryOperation const&) override;
+ void endVisit(FunctionCall const&) override;
+ void endVisit(NewExpression const&) override;
+ void endVisit(MemberAccess const&) override;
+ void endVisit(IndexAccess const&) override;
+ void endVisit(Identifier const&) override;
+ void endVisit(ElementaryTypeNameExpression const&) override;
+ void endVisit(Literal const&) override;
+
+private:
+ void printSourcePart(ASTNode const& _node);
+ void printType(Expression const& _expression);
+ std::string indentation() const;
+ void writeLine(std::string const& _line);
+ bool goDeeper() { m_indentation++; return true; }
+
+ int m_indentation;
+ std::string m_source;
+ ASTNode const* m_ast;
+ GasEstimator::ASTGasConsumption m_gasCosts;
+ std::ostream* m_ostream;
+};
+
+}
+}
diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp
new file mode 100644
index 00000000..e9b70b62
--- /dev/null
+++ b/libsolidity/ast/ASTUtils.cpp
@@ -0,0 +1,48 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Utilities to work with the AST.
+ */
+
+#include <libsolidity/ast/ASTUtils.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+
+ASTNode const* LocationFinder::leastUpperBound()
+{
+ m_bestMatch = nullptr;
+ for (ASTNode const* rootNode: m_rootNodes)
+ rootNode->accept(*this);
+
+ return m_bestMatch;
+}
+
+bool LocationFinder::visitNode(const ASTNode& _node)
+{
+ if (_node.location().contains(m_location))
+ {
+ m_bestMatch = &_node;
+ return true;
+ }
+ return false;
+}
diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h
new file mode 100644
index 00000000..237537ec
--- /dev/null
+++ b/libsolidity/ast/ASTUtils.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Utilities to work with the AST.
+ */
+
+#pragma once
+
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class LocationFinder: private ASTConstVisitor
+{
+public:
+ LocationFinder(SourceLocation const& _location, std::vector<ASTNode const*> _rootNodes):
+ m_rootNodes(_rootNodes), m_location(_location)
+ {
+ }
+
+ /// @returns the "closest" (in the sense of most-leafward) AST node which is a descendant of
+ /// _node and whose source location contains _location.
+ ASTNode const* leastUpperBound();
+
+private:
+ bool visitNode(ASTNode const& _node);
+
+ std::vector<ASTNode const*> m_rootNodes;
+ SourceLocation m_location;
+ ASTNode const* m_bestMatch = nullptr;
+};
+
+}
+}
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
new file mode 100644
index 00000000..625f395d
--- /dev/null
+++ b/libsolidity/ast/ASTVisitor.h
@@ -0,0 +1,302 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * AST visitor base class.
+ */
+
+#pragma once
+
+#include <string>
+#include <functional>
+#include <vector>
+#include <libsolidity/ast/AST.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Visitor interface for the abstract syntax tree. This class is tightly bound to the
+ * implementation of @ref ASTNode::accept and its overrides. After a call to
+ * @ref ASTNode::accept, the function visit for the appropriate parameter is called and then
+ * (if it returns true) this continues recursively for all child nodes in document order
+ * (there is an exception for contracts). After all child nodes have been visited, endVisit is
+ * called for the node.
+ */
+class ASTVisitor
+{
+public:
+ virtual bool visit(SourceUnit& _node) { return visitNode(_node); }
+ virtual bool visit(ImportDirective& _node) { return visitNode(_node); }
+ virtual bool visit(ContractDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); }
+ virtual bool visit(UsingForDirective& _node) { return visitNode(_node); }
+ virtual bool visit(StructDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(EnumDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(EnumValue& _node) { return visitNode(_node); }
+ virtual bool visit(ParameterList& _node) { return visitNode(_node); }
+ virtual bool visit(FunctionDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(VariableDeclaration& _node) { return visitNode(_node); }
+ virtual bool visit(ModifierDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); }
+ virtual bool visit(EventDefinition& _node) { return visitNode(_node); }
+ virtual bool visit(TypeName& _node) { return visitNode(_node); }
+ virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); }
+ virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
+ virtual bool visit(Mapping& _node) { return visitNode(_node); }
+ virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); }
+ virtual bool visit(Block& _node) { return visitNode(_node); }
+ virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); }
+ virtual bool visit(IfStatement& _node) { return visitNode(_node); }
+ virtual bool visit(WhileStatement& _node) { return visitNode(_node); }
+ virtual bool visit(ForStatement& _node) { return visitNode(_node); }
+ virtual bool visit(Continue& _node) { return visitNode(_node); }
+ virtual bool visit(Break& _node) { return visitNode(_node); }
+ virtual bool visit(Return& _node) { return visitNode(_node); }
+ virtual bool visit(Throw& _node) { return visitNode(_node); }
+ virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
+ virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
+ virtual bool visit(Conditional& _node) { return visitNode(_node); }
+ virtual bool visit(Assignment& _node) { return visitNode(_node); }
+ virtual bool visit(TupleExpression& _node) { return visitNode(_node); }
+ virtual bool visit(UnaryOperation& _node) { return visitNode(_node); }
+ virtual bool visit(BinaryOperation& _node) { return visitNode(_node); }
+ virtual bool visit(FunctionCall& _node) { return visitNode(_node); }
+ virtual bool visit(NewExpression& _node) { return visitNode(_node); }
+ virtual bool visit(MemberAccess& _node) { return visitNode(_node); }
+ virtual bool visit(IndexAccess& _node) { return visitNode(_node); }
+ virtual bool visit(Identifier& _node) { return visitNode(_node); }
+ virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
+ virtual bool visit(Literal& _node) { return visitNode(_node); }
+
+ virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); }
+ virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); }
+ virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); }
+ virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); }
+ virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(EnumValue& _node) { endVisitNode(_node); }
+ virtual void endVisit(ParameterList& _node) { endVisitNode(_node); }
+ virtual void endVisit(FunctionDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(VariableDeclaration& _node) { endVisitNode(_node); }
+ virtual void endVisit(ModifierDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); }
+ virtual void endVisit(EventDefinition& _node) { endVisitNode(_node); }
+ virtual void endVisit(TypeName& _node) { endVisitNode(_node); }
+ virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); }
+ virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
+ virtual void endVisit(Mapping& _node) { endVisitNode(_node); }
+ virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); }
+ virtual void endVisit(Block& _node) { endVisitNode(_node); }
+ virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(IfStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(WhileStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(ForStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(Continue& _node) { endVisitNode(_node); }
+ virtual void endVisit(Break& _node) { endVisitNode(_node); }
+ virtual void endVisit(Return& _node) { endVisitNode(_node); }
+ virtual void endVisit(Throw& _node) { endVisitNode(_node); }
+ virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
+ virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
+ virtual void endVisit(Assignment& _node) { endVisitNode(_node); }
+ virtual void endVisit(TupleExpression& _node) { endVisitNode(_node); }
+ virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); }
+ virtual void endVisit(BinaryOperation& _node) { endVisitNode(_node); }
+ virtual void endVisit(FunctionCall& _node) { endVisitNode(_node); }
+ virtual void endVisit(NewExpression& _node) { endVisitNode(_node); }
+ virtual void endVisit(MemberAccess& _node) { endVisitNode(_node); }
+ virtual void endVisit(IndexAccess& _node) { endVisitNode(_node); }
+ virtual void endVisit(Identifier& _node) { endVisitNode(_node); }
+ virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
+ virtual void endVisit(Literal& _node) { endVisitNode(_node); }
+
+protected:
+ /// Generic function called by default for each node, to be overridden by derived classes
+ /// if behaviour unspecific to a node type is desired.
+ virtual bool visitNode(ASTNode&) { return true; }
+ /// Generic function called by default for each node, to be overridden by derived classes
+ /// if behaviour unspecific to a node type is desired.
+ virtual void endVisitNode(ASTNode&) { }
+};
+
+class ASTConstVisitor
+{
+public:
+ virtual bool visit(SourceUnit const& _node) { return visitNode(_node); }
+ virtual bool visit(ImportDirective const& _node) { return visitNode(_node); }
+ virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); }
+ virtual bool visit(StructDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(UsingForDirective const& _node) { return visitNode(_node); }
+ virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(EnumValue const& _node) { return visitNode(_node); }
+ virtual bool visit(ParameterList const& _node) { return visitNode(_node); }
+ virtual bool visit(FunctionDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(VariableDeclaration const& _node) { return visitNode(_node); }
+ virtual bool visit(ModifierDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(ModifierInvocation const& _node) { return visitNode(_node); }
+ virtual bool visit(EventDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(TypeName const& _node) { return visitNode(_node); }
+ virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); }
+ virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); }
+ virtual bool visit(Mapping const& _node) { return visitNode(_node); }
+ virtual bool visit(ArrayTypeName const& _node) { return visitNode(_node); }
+ virtual bool visit(Block const& _node) { return visitNode(_node); }
+ virtual bool visit(PlaceholderStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(IfStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(WhileStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(ForStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(Continue const& _node) { return visitNode(_node); }
+ virtual bool visit(Break const& _node) { return visitNode(_node); }
+ virtual bool visit(Return const& _node) { return visitNode(_node); }
+ virtual bool visit(Throw const& _node) { return visitNode(_node); }
+ virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
+ virtual bool visit(Conditional const& _node) { return visitNode(_node); }
+ virtual bool visit(Assignment const& _node) { return visitNode(_node); }
+ virtual bool visit(TupleExpression const& _node) { return visitNode(_node); }
+ virtual bool visit(UnaryOperation const& _node) { return visitNode(_node); }
+ virtual bool visit(BinaryOperation const& _node) { return visitNode(_node); }
+ virtual bool visit(FunctionCall const& _node) { return visitNode(_node); }
+ virtual bool visit(NewExpression const& _node) { return visitNode(_node); }
+ virtual bool visit(MemberAccess const& _node) { return visitNode(_node); }
+ virtual bool visit(IndexAccess const& _node) { return visitNode(_node); }
+ virtual bool visit(Identifier const& _node) { return visitNode(_node); }
+ virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
+ virtual bool visit(Literal const& _node) { return visitNode(_node); }
+
+ virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); }
+ virtual void endVisit(UsingForDirective const& _node) { endVisitNode(_node); }
+ virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ParameterList const& _node) { endVisitNode(_node); }
+ virtual void endVisit(FunctionDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(VariableDeclaration const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ModifierDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); }
+ virtual void endVisit(EventDefinition const& _node) { endVisitNode(_node); }
+ virtual void endVisit(TypeName const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); }
+ virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Mapping const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ArrayTypeName const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Block const& _node) { endVisitNode(_node); }
+ virtual void endVisit(PlaceholderStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(IfStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(WhileStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Continue const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Break const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Return const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
+ virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Conditional const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Assignment const& _node) { endVisitNode(_node); }
+ virtual void endVisit(TupleExpression const& _node) { endVisitNode(_node); }
+ virtual void endVisit(UnaryOperation const& _node) { endVisitNode(_node); }
+ virtual void endVisit(BinaryOperation const& _node) { endVisitNode(_node); }
+ virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); }
+ virtual void endVisit(NewExpression const& _node) { endVisitNode(_node); }
+ virtual void endVisit(MemberAccess const& _node) { endVisitNode(_node); }
+ virtual void endVisit(IndexAccess const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Identifier const& _node) { endVisitNode(_node); }
+ virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
+ virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
+
+protected:
+ /// Generic function called by default for each node, to be overridden by derived classes
+ /// if behaviour unspecific to a node type is desired.
+ virtual bool visitNode(ASTNode const&) { return true; }
+ /// Generic function called by default for each node, to be overridden by derived classes
+ /// if behaviour unspecific to a node type is desired.
+ virtual void endVisitNode(ASTNode const&) { }
+};
+
+/**
+ * Utility class that accepts std::functions and calls them for visitNode and endVisitNode.
+ */
+class SimpleASTVisitor: public ASTConstVisitor
+{
+public:
+ SimpleASTVisitor(
+ std::function<bool(ASTNode const&)> _onVisit,
+ std::function<void(ASTNode const&)> _onEndVisit
+ ): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {}
+
+protected:
+ virtual bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; }
+ virtual void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); }
+
+private:
+ std::function<bool(ASTNode const&)> m_onVisit;
+ std::function<void(ASTNode const&)> m_onEndVisit;
+};
+
+/**
+ * Utility class that visits the AST in depth-first order and calls a function on each node and each edge.
+ * Child nodes are only visited if the node callback of the parent returns true.
+ * The node callback of a parent is called before any edge or node callback involving the children.
+ * The edge callbacks of all children are called before the edge callback of the parent.
+ * This way, the node callback can be used as an initializing callback and the edge callbacks can be
+ * used to compute a "reduce" function.
+ */
+class ASTReduce: public ASTConstVisitor
+{
+public:
+ /**
+ * Constructs a new ASTReduce object with the given callback functions.
+ * @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper
+ * @param _onEdge called for each edge with (parent, child)
+ */
+ ASTReduce(
+ std::function<bool(ASTNode const&)> _onNode,
+ std::function<void(ASTNode const&, ASTNode const&)> _onEdge
+ ): m_onNode(_onNode), m_onEdge(_onEdge)
+ {
+ }
+
+protected:
+ bool visitNode(ASTNode const& _node) override
+ {
+ m_parents.push_back(&_node);
+ return m_onNode(_node);
+ }
+ void endVisitNode(ASTNode const& _node) override
+ {
+ m_parents.pop_back();
+ if (!m_parents.empty())
+ m_onEdge(*m_parents.back(), _node);
+ }
+
+private:
+ std::vector<ASTNode const*> m_parents;
+ std::function<bool(ASTNode const&)> m_onNode;
+ std::function<void(ASTNode const&, ASTNode const&)> m_onEdge;
+};
+
+}
+}
diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h
new file mode 100644
index 00000000..dee9d5b1
--- /dev/null
+++ b/libsolidity/ast/AST_accept.h
@@ -0,0 +1,755 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that
+ * file with these mechanical implementations.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+void SourceUnit::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_nodes, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void SourceUnit::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_nodes, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void ImportDirective::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ImportDirective::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ContractDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ listAccept(m_baseContracts, _visitor);
+ listAccept(m_subNodes, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ContractDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ listAccept(m_baseContracts, _visitor);
+ listAccept(m_subNodes, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void InheritanceSpecifier::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void EnumDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void EnumDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void EnumValue::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void EnumValue::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void UsingForDirective::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_libraryName->accept(_visitor);
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void UsingForDirective::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_libraryName->accept(_visitor);
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void StructDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void StructDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_members, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void ParameterList::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_parameters, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void ParameterList::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_parameters, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void FunctionDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ if (m_returnParameters)
+ m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
+ if (m_body)
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ if (m_returnParameters)
+ m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
+ if (m_body)
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void VariableDeclaration::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ if (m_value)
+ m_value->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ if (m_value)
+ m_value->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_parameters->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierInvocation::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void EventDefinition::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ m_parameters->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void EventDefinition::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ m_parameters->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void TypeName::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void TypeName::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ElementaryTypeName::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void UserDefinedTypeName::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Mapping::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_keyType->accept(_visitor);
+ m_valueType->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Mapping::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_keyType->accept(_visitor);
+ m_valueType->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ArrayTypeName::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseType->accept(_visitor);
+ if (m_length)
+ m_length->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ArrayTypeName::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_baseType->accept(_visitor);
+ if (m_length)
+ m_length->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Block::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ listAccept(m_statements, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void Block::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ listAccept(m_statements, _visitor);
+ _visitor.endVisit(*this);
+}
+
+void PlaceholderStatement::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void IfStatement::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_trueBody->accept(_visitor);
+ if (m_falseBody)
+ m_falseBody->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void IfStatement::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_trueBody->accept(_visitor);
+ if (m_falseBody)
+ m_falseBody->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void WhileStatement::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void WhileStatement::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ForStatement::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ if (m_initExpression)
+ m_initExpression->accept(_visitor);
+ if (m_condExpression)
+ m_condExpression->accept(_visitor);
+ if (m_loopExpression)
+ m_loopExpression->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ForStatement::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ if (m_initExpression)
+ m_initExpression->accept(_visitor);
+ if (m_condExpression)
+ m_condExpression->accept(_visitor);
+ if (m_loopExpression)
+ m_loopExpression->accept(_visitor);
+ m_body->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Continue::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Continue::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Break::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Break::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Return::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ if (m_expression)
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void Return::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ if (m_expression)
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void Throw::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Throw::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ExpressionStatement::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ if (m_expression)
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ if (m_expression)
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void VariableDeclarationStatement::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ for (ASTPointer<VariableDeclaration> const& var: m_variables)
+ if (var)
+ var->accept(_visitor);
+ if (m_initialValue)
+ m_initialValue->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ for (ASTPointer<VariableDeclaration> const& var: m_variables)
+ if (var)
+ var->accept(_visitor);
+ if (m_initialValue)
+ m_initialValue->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Conditional::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_trueExpression->accept(_visitor);
+ m_falseExpression->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Conditional::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_condition->accept(_visitor);
+ m_trueExpression->accept(_visitor);
+ m_falseExpression->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Assignment::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_leftHandSide->accept(_visitor);
+ m_rightHandSide->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Assignment::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_leftHandSide->accept(_visitor);
+ m_rightHandSide->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void TupleExpression::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ for (auto const& component: m_components)
+ if (component)
+ component->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void TupleExpression::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ for (auto const& component: m_components)
+ if (component)
+ component->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void UnaryOperation::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ m_subExpression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void UnaryOperation::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ m_subExpression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void BinaryOperation::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_left->accept(_visitor);
+ m_right->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void BinaryOperation::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_left->accept(_visitor);
+ m_right->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void FunctionCall::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_expression->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void FunctionCall::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_expression->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void NewExpression::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ m_typeName->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void NewExpression::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ m_typeName->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void MemberAccess::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void MemberAccess::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ m_expression->accept(_visitor);
+ _visitor.endVisit(*this);
+}
+
+void IndexAccess::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_base->accept(_visitor);
+ if (m_index)
+ m_index->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void IndexAccess::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_base->accept(_visitor);
+ if (m_index)
+ m_index->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void Identifier::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Identifier::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Literal::accept(ASTVisitor& _visitor)
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+void Literal::accept(ASTConstVisitor& _visitor) const
+{
+ _visitor.visit(*this);
+ _visitor.endVisit(*this);
+}
+
+}
+}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
new file mode 100644
index 00000000..4dc1eb13
--- /dev/null
+++ b/libsolidity/ast/Types.cpp
@@ -0,0 +1,2029 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity data types
+ */
+
+#include <libsolidity/ast/Types.h>
+#include <limits>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libdevcore/CommonIO.h>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/SHA3.h>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+void StorageOffsets::computeOffsets(TypePointers const& _types)
+{
+ bigint slotOffset = 0;
+ unsigned byteOffset = 0;
+ map<size_t, pair<u256, unsigned>> offsets;
+ for (size_t i = 0; i < _types.size(); ++i)
+ {
+ TypePointer const& type = _types[i];
+ if (!type->canBeStored())
+ continue;
+ if (byteOffset + type->storageBytes() > 32)
+ {
+ // would overflow, go to next slot
+ ++slotOffset;
+ byteOffset = 0;
+ }
+ if (slotOffset >= bigint(1) << 256)
+ BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Object too large for storage."));
+ offsets[i] = make_pair(u256(slotOffset), byteOffset);
+ solAssert(type->storageSize() >= 1, "Invalid storage size.");
+ if (type->storageSize() == 1 && byteOffset + type->storageBytes() <= 32)
+ byteOffset += type->storageBytes();
+ else
+ {
+ slotOffset += type->storageSize();
+ byteOffset = 0;
+ }
+ }
+ if (byteOffset > 0)
+ ++slotOffset;
+ if (slotOffset >= bigint(1) << 256)
+ BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Object too large for storage."));
+ m_storageSize = u256(slotOffset);
+ swap(m_offsets, offsets);
+}
+
+pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const
+{
+ if (m_offsets.count(_index))
+ return &m_offsets.at(_index);
+ else
+ return nullptr;
+}
+
+MemberList& MemberList::operator=(MemberList&& _other)
+{
+ assert(&_other != this);
+
+ m_memberTypes = move(_other.m_memberTypes);
+ m_storageOffsets = move(_other.m_storageOffsets);
+ return *this;
+}
+
+void MemberList::combine(MemberList const & _other)
+{
+ m_memberTypes += _other.m_memberTypes;
+}
+
+pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const
+{
+ if (!m_storageOffsets)
+ {
+ TypePointers memberTypes;
+ memberTypes.reserve(m_memberTypes.size());
+ for (auto const& member: m_memberTypes)
+ memberTypes.push_back(member.type);
+ m_storageOffsets.reset(new StorageOffsets());
+ m_storageOffsets->computeOffsets(memberTypes);
+ }
+ for (size_t index = 0; index < m_memberTypes.size(); ++index)
+ if (m_memberTypes[index].name == _name)
+ return m_storageOffsets->offset(index);
+ return nullptr;
+}
+
+u256 const& MemberList::storageSize() const
+{
+ // trigger lazy computation
+ memberStorageOffset("");
+ return m_storageOffsets->storageSize();
+}
+
+TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
+{
+ solAssert(Token::isElementaryTypeName(_type.token()),
+ "Expected an elementary type name but got " + _type.toString()
+ );
+
+ Token::Value token = _type.token();
+ unsigned int m = _type.firstNumber();
+
+ switch (token)
+ {
+ case Token::IntM:
+ return make_shared<IntegerType>(m, IntegerType::Modifier::Signed);
+ case Token::UIntM:
+ return make_shared<IntegerType>(m, IntegerType::Modifier::Unsigned);
+ case Token::BytesM:
+ return make_shared<FixedBytesType>(m);
+ case Token::Int:
+ return make_shared<IntegerType>(256, IntegerType::Modifier::Signed);
+ case Token::UInt:
+ return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned);
+ case Token::Byte:
+ return make_shared<FixedBytesType>(1);
+ case Token::Address:
+ return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
+ case Token::Bool:
+ return make_shared<BoolType>();
+ case Token::Bytes:
+ return make_shared<ArrayType>(DataLocation::Storage);
+ case Token::String:
+ return make_shared<ArrayType>(DataLocation::Storage, true);
+ //no types found
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
+ "Unable to convert elementary typename " + _type.toString() + " to type."
+ ));
+ }
+}
+
+TypePointer Type::fromElementaryTypeName(string const& _name)
+{
+ unsigned short firstNum;
+ unsigned short secondNum;
+ Token::Value token;
+ tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(_name);
+ return fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
+}
+
+TypePointer Type::forLiteral(Literal const& _literal)
+{
+ switch (_literal.token())
+ {
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
+ return make_shared<BoolType>();
+ case Token::Number:
+ if (!IntegerConstantType::isValidLiteral(_literal))
+ return TypePointer();
+ return make_shared<IntegerConstantType>(_literal);
+ case Token::StringLiteral:
+ return make_shared<StringLiteralType>(_literal);
+ default:
+ return shared_ptr<Type>();
+ }
+}
+
+TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
+{
+ if (_b->isImplicitlyConvertibleTo(*_a))
+ return _a;
+ else if (_a->isImplicitlyConvertibleTo(*_b))
+ return _b;
+ else
+ return TypePointer();
+}
+
+MemberList const& Type::members(ContractDefinition const* _currentScope) const
+{
+ if (!m_members[_currentScope])
+ {
+ MemberList::MemberMap members = nativeMembers(_currentScope);
+ if (_currentScope)
+ members += boundFunctions(*this, *_currentScope);
+ m_members[_currentScope] = unique_ptr<MemberList>(new MemberList(move(members)));
+ }
+ return *m_members[_currentScope];
+}
+
+MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
+{
+ // Normalise data location of type.
+ TypePointer type = ReferenceType::copyForLocationIfReference(DataLocation::Storage, _type.shared_from_this());
+ set<Declaration const*> seenFunctions;
+ MemberList::MemberMap members;
+ for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts)
+ for (UsingForDirective const* ufd: contract->usingForDirectives())
+ {
+ if (ufd->typeName() && *type != *ReferenceType::copyForLocationIfReference(
+ DataLocation::Storage,
+ ufd->typeName()->annotation().type
+ ))
+ continue;
+ auto const& library = dynamic_cast<ContractDefinition const&>(
+ *ufd->libraryName().annotation().referencedDeclaration
+ );
+ for (auto const& it: library.interfaceFunctions())
+ {
+ FunctionType const& funType = *it.second;
+ solAssert(funType.hasDeclaration(), "Tried to bind function without declaration.");
+ if (seenFunctions.count(&funType.declaration()))
+ continue;
+ seenFunctions.insert(&funType.declaration());
+ if (auto fun = funType.asMemberFunction(true, true))
+ members.push_back(MemberList::Member(
+ funType.declaration().name(),
+ fun,
+ &funType.declaration()
+ ));
+ }
+ }
+ return members;
+}
+
+IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
+ m_bits(_bits), m_modifier(_modifier)
+{
+ if (isAddress())
+ m_bits = 160;
+ solAssert(
+ m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0,
+ "Invalid bit number for integer type: " + dev::toString(_bits)
+ );
+}
+
+bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+ IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
+ if (convertTo.m_bits < m_bits)
+ return false;
+ if (isAddress())
+ return convertTo.isAddress();
+ else if (isSigned())
+ return convertTo.isSigned();
+ else
+ return !convertTo.isSigned() || convertTo.m_bits > m_bits;
+}
+
+bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return _convertTo.category() == category() ||
+ _convertTo.category() == Category::Contract ||
+ _convertTo.category() == Category::Enum ||
+ _convertTo.category() == Category::FixedBytes;
+}
+
+TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
+{
+ // "delete" is ok for all integer types
+ if (_operator == Token::Delete)
+ return make_shared<TupleType>();
+ // no further unary operators for addresses
+ else if (isAddress())
+ return TypePointer();
+ // for non-address integers, we allow +, -, ++ and --
+ else if (_operator == Token::Add || _operator == Token::Sub ||
+ _operator == Token::Inc || _operator == Token::Dec ||
+ _operator == Token::After || _operator == Token::BitNot)
+ return shared_from_this();
+ else
+ return TypePointer();
+}
+
+bool IntegerType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ IntegerType const& other = dynamic_cast<IntegerType const&>(_other);
+ return other.m_bits == m_bits && other.m_modifier == m_modifier;
+}
+
+string IntegerType::toString(bool) const
+{
+ if (isAddress())
+ return "address";
+ string prefix = isSigned() ? "int" : "uint";
+ return prefix + dev::toString(m_bits);
+}
+
+TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ if (_other->category() != Category::IntegerConstant && _other->category() != category())
+ return TypePointer();
+ auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other));
+
+ if (!commonType)
+ return TypePointer();
+
+ // All integer types can be compared
+ if (Token::isCompareOp(_operator))
+ return commonType;
+ if (Token::isBooleanOp(_operator))
+ return TypePointer();
+ // Nothing else can be done with addresses
+ if (commonType->isAddress())
+ return TypePointer();
+
+ return commonType;
+}
+
+MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const
+{
+ if (isAddress())
+ return {
+ {"balance", make_shared<IntegerType >(256)},
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
+ {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
+ {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
+ };
+ else
+ return MemberList::MemberMap();
+}
+
+bool IntegerConstantType::isValidLiteral(const Literal& _literal)
+{
+ try
+ {
+ bigint x(_literal.value());
+ }
+ catch (...)
+ {
+ return false;
+ }
+ return true;
+}
+
+IntegerConstantType::IntegerConstantType(Literal const& _literal)
+{
+ m_value = bigint(_literal.value());
+
+ switch (_literal.subDenomination())
+ {
+ case Literal::SubDenomination::Wei:
+ case Literal::SubDenomination::Second:
+ case Literal::SubDenomination::None:
+ break;
+ case Literal::SubDenomination::Szabo:
+ m_value *= bigint("1000000000000");
+ break;
+ case Literal::SubDenomination::Finney:
+ m_value *= bigint("1000000000000000");
+ break;
+ case Literal::SubDenomination::Ether:
+ m_value *= bigint("1000000000000000000");
+ break;
+ case Literal::SubDenomination::Minute:
+ m_value *= bigint("60");
+ break;
+ case Literal::SubDenomination::Hour:
+ m_value *= bigint("3600");
+ break;
+ case Literal::SubDenomination::Day:
+ m_value *= bigint("86400");
+ break;
+ case Literal::SubDenomination::Week:
+ m_value *= bigint("604800");
+ break;
+ case Literal::SubDenomination::Year:
+ m_value *= bigint("31536000");
+ break;
+ }
+}
+
+bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo))
+ {
+ if (m_value == 0)
+ return true;
+ int forSignBit = (targetType->isSigned() ? 1 : 0);
+ if (m_value > 0)
+ {
+ if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)))
+ return true;
+ }
+ else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit)))
+ return true;
+ return false;
+ }
+ else if (_convertTo.category() == Category::FixedBytes)
+ {
+ FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
+ return fixedBytes.numBytes() * 8 >= integerType()->numBits();
+ }
+ else
+ return false;
+}
+
+bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ TypePointer intType = integerType();
+ return intType && intType->isExplicitlyConvertibleTo(_convertTo);
+}
+
+TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const
+{
+ bigint value;
+ switch (_operator)
+ {
+ case Token::BitNot:
+ value = ~m_value;
+ break;
+ case Token::Add:
+ value = m_value;
+ break;
+ case Token::Sub:
+ value = -m_value;
+ break;
+ case Token::After:
+ return shared_from_this();
+ default:
+ return TypePointer();
+ }
+ return make_shared<IntegerConstantType>(value);
+}
+
+TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ if (_other->category() == Category::Integer)
+ {
+ shared_ptr<IntegerType const> intType = integerType();
+ if (!intType)
+ return TypePointer();
+ return intType->binaryOperatorResult(_operator, _other);
+ }
+ else if (_other->category() != category())
+ return TypePointer();
+
+ IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other);
+ if (Token::isCompareOp(_operator))
+ {
+ shared_ptr<IntegerType const> thisIntegerType = integerType();
+ shared_ptr<IntegerType const> otherIntegerType = other.integerType();
+ if (!thisIntegerType || !otherIntegerType)
+ return TypePointer();
+ return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType);
+ }
+ else
+ {
+ bigint value;
+ switch (_operator)
+ {
+ case Token::BitOr:
+ value = m_value | other.m_value;
+ break;
+ case Token::BitXor:
+ value = m_value ^ other.m_value;
+ break;
+ case Token::BitAnd:
+ value = m_value & other.m_value;
+ break;
+ case Token::Add:
+ value = m_value + other.m_value;
+ break;
+ case Token::Sub:
+ value = m_value - other.m_value;
+ break;
+ case Token::Mul:
+ value = m_value * other.m_value;
+ break;
+ case Token::Div:
+ if (other.m_value == 0)
+ return TypePointer();
+ value = m_value / other.m_value;
+ break;
+ case Token::Mod:
+ if (other.m_value == 0)
+ return TypePointer();
+ value = m_value % other.m_value;
+ break;
+ case Token::Exp:
+ if (other.m_value < 0)
+ return TypePointer();
+ else if (other.m_value > numeric_limits<unsigned int>::max())
+ return TypePointer();
+ else
+ value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>());
+ break;
+ default:
+ return TypePointer();
+ }
+ return make_shared<IntegerConstantType>(value);
+ }
+}
+
+bool IntegerConstantType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value;
+}
+
+string IntegerConstantType::toString(bool) const
+{
+ return "int_const " + m_value.str();
+}
+
+u256 IntegerConstantType::literalValue(Literal const*) const
+{
+ u256 value;
+ // we ignore the literal and hope that the type was correctly determined
+ solAssert(m_value <= u256(-1), "Integer constant too large.");
+ solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small.");
+
+ if (m_value >= 0)
+ value = u256(m_value);
+ else
+ value = s2u(s256(m_value));
+
+ return value;
+}
+
+TypePointer IntegerConstantType::mobileType() const
+{
+ auto intType = integerType();
+ solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false));
+ return intType;
+}
+
+shared_ptr<IntegerType const> IntegerConstantType::integerType() const
+{
+ bigint value = m_value;
+ bool negative = (value < 0);
+ if (negative) // convert to positive number of same bit requirements
+ value = ((-value) - 1) << 1;
+ if (value > u256(-1))
+ return shared_ptr<IntegerType const>();
+ else
+ return make_shared<IntegerType>(
+ max(bytesRequired(value), 1u) * 8,
+ negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
+ );
+}
+
+StringLiteralType::StringLiteralType(Literal const& _literal):
+ m_value(_literal.value())
+{
+}
+
+bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
+ return size_t(fixedBytes->numBytes()) >= m_value.size();
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo))
+ return
+ arrayType->isByteArray() &&
+ !(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer());
+ else
+ return false;
+}
+
+bool StringLiteralType::operator==(const Type& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ return m_value == dynamic_cast<StringLiteralType const&>(_other).m_value;
+}
+
+TypePointer StringLiteralType::mobileType() const
+{
+ return make_shared<ArrayType>(DataLocation::Memory, true);
+}
+
+shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
+{
+ if (_literal.length() <= 32)
+ return make_shared<FixedBytesType>(_literal.length());
+ return shared_ptr<FixedBytesType>();
+}
+
+FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes)
+{
+ solAssert(m_bytes >= 0 && m_bytes <= 32,
+ "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes));
+}
+
+bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+ FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo);
+ return convertTo.m_bytes >= m_bytes;
+}
+
+bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return _convertTo.category() == Category::Integer ||
+ _convertTo.category() == Category::Contract ||
+ _convertTo.category() == category();
+}
+
+TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
+{
+ // "delete" and "~" is okay for FixedBytesType
+ if (_operator == Token::Delete)
+ return make_shared<TupleType>();
+ else if (_operator == Token::BitNot)
+ return shared_from_this();
+
+ return TypePointer();
+}
+
+TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ auto commonType = dynamic_pointer_cast<FixedBytesType const>(Type::commonType(shared_from_this(), _other));
+ if (!commonType)
+ return TypePointer();
+
+ // FixedBytes can be compared and have bitwise operators applied to them
+ if (Token::isCompareOp(_operator) || Token::isBitOp(_operator))
+ return commonType;
+
+ return TypePointer();
+}
+
+MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) const
+{
+ return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}};
+}
+
+bool FixedBytesType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ FixedBytesType const& other = dynamic_cast<FixedBytesType const&>(_other);
+ return other.m_bytes == m_bytes;
+}
+
+bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ // conversion to integer is fine, but not to address
+ // this is an example of explicit conversions being not transitive (though implicit should be)
+ if (_convertTo.category() == category())
+ {
+ IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
+ if (!convertTo.isAddress())
+ return true;
+ }
+ return isImplicitlyConvertibleTo(_convertTo);
+}
+
+u256 BoolType::literalValue(Literal const* _literal) const
+{
+ solAssert(_literal, "");
+ if (_literal->token() == Token::TrueLiteral)
+ return u256(1);
+ else if (_literal->token() == Token::FalseLiteral)
+ return u256(0);
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
+}
+
+TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
+{
+ if (_operator == Token::Delete)
+ return make_shared<TupleType>();
+ return (_operator == Token::Not) ? shared_from_this() : TypePointer();
+}
+
+TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ if (category() != _other->category())
+ return TypePointer();
+ if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or)
+ return _other;
+ else
+ return TypePointer();
+}
+
+bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (*this == _convertTo)
+ return true;
+ if (_convertTo.category() == Category::Integer)
+ return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
+ if (_convertTo.category() == Category::Contract)
+ {
+ auto const& bases = contractDefinition().annotation().linearizedBaseContracts;
+ if (m_super && bases.size() <= 1)
+ return false;
+ return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
+ &dynamic_cast<ContractType const&>(_convertTo).contractDefinition()) != bases.end();
+ }
+ return false;
+}
+
+bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return
+ isImplicitlyConvertibleTo(_convertTo) ||
+ _convertTo.category() == Category::Integer ||
+ _convertTo.category() == Category::Contract;
+}
+
+TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
+}
+
+TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
+{
+ if (_operator != Token::Delete)
+ return TypePointer();
+ // delete can be used on everything except calldata references or storage pointers
+ // (storage references are ok)
+ switch (location())
+ {
+ case DataLocation::CallData:
+ return TypePointer();
+ case DataLocation::Memory:
+ return make_shared<TupleType>();
+ case DataLocation::Storage:
+ return m_isPointer ? TypePointer() : make_shared<TupleType>();
+ default:
+ solAssert(false, "");
+ }
+ return TypePointer();
+}
+
+TypePointer ReferenceType::copyForLocationIfReference(DataLocation _location, TypePointer const& _type)
+{
+ if (auto type = dynamic_cast<ReferenceType const*>(_type.get()))
+ return type->copyForLocation(_location, false);
+ return _type;
+}
+
+TypePointer ReferenceType::copyForLocationIfReference(TypePointer const& _type) const
+{
+ return copyForLocationIfReference(m_location, _type);
+}
+
+string ReferenceType::stringForReferencePart() const
+{
+ switch (m_location)
+ {
+ case DataLocation::Storage:
+ return string("storage ") + (m_isPointer ? "pointer" : "ref");
+ case DataLocation::CallData:
+ return "calldata";
+ case DataLocation::Memory:
+ return "memory";
+ }
+ solAssert(false, "");
+ return "";
+}
+
+bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+ auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
+ if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
+ return false;
+ // memory/calldata to storage can be converted, but only to a direct storage reference
+ if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer())
+ return false;
+ if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
+ return false;
+ if (convertTo.location() == DataLocation::Storage && !convertTo.isPointer())
+ {
+ // Less restrictive conversion, since we need to copy anyway.
+ if (!baseType()->isImplicitlyConvertibleTo(*convertTo.baseType()))
+ return false;
+ if (convertTo.isDynamicallySized())
+ return true;
+ return !isDynamicallySized() && convertTo.length() >= length();
+ }
+ else
+ {
+ // Conversion to storage pointer or to memory, we de not copy element-for-element here, so
+ // require that the base type is the same, not only convertible.
+ // This disallows assignment of nested dynamic arrays from storage to memory for now.
+ if (
+ *copyForLocationIfReference(location(), baseType()) !=
+ *copyForLocationIfReference(location(), convertTo.baseType())
+ )
+ return false;
+ if (isDynamicallySized() != convertTo.isDynamicallySized())
+ return false;
+ // We also require that the size is the same.
+ if (!isDynamicallySized() && length() != convertTo.length())
+ return false;
+ return true;
+ }
+}
+
+bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
+{
+ if (isImplicitlyConvertibleTo(_convertTo))
+ return true;
+ // allow conversion bytes <-> string
+ if (_convertTo.category() != category())
+ return false;
+ auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
+ if (convertTo.location() != location())
+ return false;
+ if (!isByteArray() || !convertTo.isByteArray())
+ return false;
+ return true;
+}
+
+bool ArrayType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
+ if (
+ !ReferenceType::operator==(other) ||
+ other.isByteArray() != isByteArray() ||
+ other.isString() != isString() ||
+ other.isDynamicallySized() != isDynamicallySized()
+ )
+ return false;
+ if (*other.baseType() != *baseType())
+ return false;
+ return isDynamicallySized() || length() == other.length();
+}
+
+unsigned ArrayType::calldataEncodedSize(bool _padded) const
+{
+ if (isDynamicallySized())
+ return 32;
+ bigint size = bigint(length()) * (isByteArray() ? 1 : baseType()->calldataEncodedSize(_padded));
+ size = ((size + 31) / 32) * 32;
+ solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned.");
+ return unsigned(size);
+}
+
+u256 ArrayType::storageSize() const
+{
+ if (isDynamicallySized())
+ return 1;
+
+ bigint size;
+ unsigned baseBytes = baseType()->storageBytes();
+ if (baseBytes == 0)
+ size = 1;
+ else if (baseBytes < 32)
+ {
+ unsigned itemsPerSlot = 32 / baseBytes;
+ size = (bigint(length()) + (itemsPerSlot - 1)) / itemsPerSlot;
+ }
+ else
+ size = bigint(length()) * baseType()->storageSize();
+ if (size >= bigint(1) << 256)
+ BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Array too large for storage."));
+ return max<u256>(1, u256(size));
+}
+
+unsigned ArrayType::sizeOnStack() const
+{
+ if (m_location == DataLocation::CallData)
+ // offset [length] (stack top)
+ return 1 + (isDynamicallySized() ? 1 : 0);
+ else
+ // storage slot or memory offset
+ // byte offset inside storage value is omitted
+ return 1;
+}
+
+string ArrayType::toString(bool _short) const
+{
+ string ret;
+ if (isString())
+ ret = "string";
+ else if (isByteArray())
+ ret = "bytes";
+ else
+ {
+ ret = baseType()->toString(_short) + "[";
+ if (!isDynamicallySized())
+ ret += length().str();
+ ret += "]";
+ }
+ if (!_short)
+ ret += " " + stringForReferencePart();
+ return ret;
+}
+
+string ArrayType::canonicalName(bool _addDataLocation) const
+{
+ string ret;
+ if (isString())
+ ret = "string";
+ else if (isByteArray())
+ ret = "bytes";
+ else
+ {
+ ret = baseType()->canonicalName(false) + "[";
+ if (!isDynamicallySized())
+ ret += length().str();
+ ret += "]";
+ }
+ if (_addDataLocation && location() == DataLocation::Storage)
+ ret += " storage";
+ return ret;
+}
+
+MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
+{
+ MemberList::MemberMap members;
+ if (!isString())
+ {
+ members.push_back({"length", make_shared<IntegerType>(256)});
+ if (isDynamicallySized() && location() == DataLocation::Storage)
+ members.push_back({"push", make_shared<FunctionType>(
+ TypePointers{baseType()},
+ TypePointers{make_shared<IntegerType>(256)},
+ strings{string()},
+ strings{string()},
+ isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush
+ )});
+ }
+ return members;
+}
+
+TypePointer ArrayType::encodingType() const
+{
+ if (location() == DataLocation::Storage)
+ return make_shared<IntegerType>(256);
+ else
+ return this->copyForLocation(DataLocation::Memory, true);
+}
+
+TypePointer ArrayType::decodingType() const
+{
+ if (location() == DataLocation::Storage)
+ return make_shared<IntegerType>(256);
+ else
+ return shared_from_this();
+}
+
+TypePointer ArrayType::interfaceType(bool _inLibrary) const
+{
+ if (_inLibrary && location() == DataLocation::Storage)
+ return shared_from_this();
+
+ if (m_arrayKind != ArrayKind::Ordinary)
+ return this->copyForLocation(DataLocation::Memory, true);
+ TypePointer baseExt = m_baseType->interfaceType(_inLibrary);
+ if (!baseExt)
+ return TypePointer();
+ if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
+ return TypePointer();
+
+ if (isDynamicallySized())
+ return make_shared<ArrayType>(DataLocation::Memory, baseExt);
+ else
+ return make_shared<ArrayType>(DataLocation::Memory, baseExt, m_length);
+}
+
+u256 ArrayType::memorySize() const
+{
+ solAssert(!isDynamicallySized(), "");
+ solAssert(m_location == DataLocation::Memory, "");
+ bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
+ solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit u256.");
+ return u256(size);
+}
+
+TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const
+{
+ auto copy = make_shared<ArrayType>(_location);
+ copy->m_isPointer = _isPointer;
+ copy->m_arrayKind = m_arrayKind;
+ copy->m_baseType = copy->copyForLocationIfReference(m_baseType);
+ copy->m_hasDynamicLength = m_hasDynamicLength;
+ copy->m_length = m_length;
+ return copy;
+}
+
+bool ContractType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ ContractType const& other = dynamic_cast<ContractType const&>(_other);
+ return other.m_contract == m_contract && other.m_super == m_super;
+}
+
+string ContractType::toString(bool) const
+{
+ return
+ string(m_contract.isLibrary() ? "library " : "contract ") +
+ string(m_super ? "super " : "") +
+ m_contract.name();
+}
+
+string ContractType::canonicalName(bool) const
+{
+ return m_contract.annotation().canonicalName;
+}
+
+MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const
+{
+ // All address members and all interface functions
+ MemberList::MemberMap members(IntegerType(120, IntegerType::Modifier::Address).nativeMembers(nullptr));
+ if (m_super)
+ {
+ // add the most derived of all functions which are visible in derived contracts
+ for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
+ for (FunctionDefinition const* function: base->definedFunctions())
+ {
+ if (!function->isVisibleInDerivedContracts())
+ continue;
+ auto functionType = make_shared<FunctionType>(*function, true);
+ bool functionWithEqualArgumentsFound = false;
+ for (auto const& member: members)
+ {
+ if (member.name != function->name())
+ continue;
+ auto memberType = dynamic_cast<FunctionType const*>(member.type.get());
+ solAssert(!!memberType, "Override changes type.");
+ if (!memberType->hasEqualArgumentTypes(*functionType))
+ continue;
+ functionWithEqualArgumentsFound = true;
+ break;
+ }
+ if (!functionWithEqualArgumentsFound)
+ members.push_back(MemberList::Member(
+ function->name(),
+ functionType,
+ function
+ ));
+ }
+ }
+ else
+ {
+ for (auto const& it: m_contract.interfaceFunctions())
+ members.push_back(MemberList::Member(
+ it.second->declaration().name(),
+ it.second->asMemberFunction(m_contract.isLibrary()),
+ &it.second->declaration()
+ ));
+ }
+ return members;
+}
+
+shared_ptr<FunctionType const> const& ContractType::constructorType() const
+{
+ if (!m_constructorType)
+ {
+ FunctionDefinition const* constructor = m_contract.constructor();
+ if (constructor)
+ m_constructorType = make_shared<FunctionType>(*constructor);
+ else
+ m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers());
+ }
+ return m_constructorType;
+}
+
+vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables() const
+{
+ vector<VariableDeclaration const*> variables;
+ for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
+ for (VariableDeclaration const* variable: contract->stateVariables())
+ if (!variable->isConstant())
+ variables.push_back(variable);
+ TypePointers types;
+ for (auto variable: variables)
+ types.push_back(variable->annotation().type);
+ StorageOffsets offsets;
+ offsets.computeOffsets(types);
+
+ vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
+ for (size_t index = 0; index < variables.size(); ++index)
+ if (auto const* offset = offsets.offset(index))
+ variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
+ return variablesAndOffsets;
+}
+
+bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+ auto& convertTo = dynamic_cast<StructType const&>(_convertTo);
+ // memory/calldata to storage can be converted, but only to a direct storage reference
+ if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer())
+ return false;
+ if (convertTo.location() == DataLocation::CallData && location() != convertTo.location())
+ return false;
+ return this->m_struct == convertTo.m_struct;
+}
+
+bool StructType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ StructType const& other = dynamic_cast<StructType const&>(_other);
+ return ReferenceType::operator==(other) && other.m_struct == m_struct;
+}
+
+unsigned StructType::calldataEncodedSize(bool _padded) const
+{
+ unsigned size = 0;
+ for (auto const& member: members(nullptr))
+ if (!member.type->canLiveOutsideStorage())
+ return 0;
+ else
+ {
+ unsigned memberSize = member.type->calldataEncodedSize(_padded);
+ if (memberSize == 0)
+ return 0;
+ size += memberSize;
+ }
+ return size;
+}
+
+u256 StructType::memorySize() const
+{
+ u256 size;
+ for (auto const& member: members(nullptr))
+ if (member.type->canLiveOutsideStorage())
+ size += member.type->memoryHeadSize();
+ return size;
+}
+
+u256 StructType::storageSize() const
+{
+ return max<u256>(1, members(nullptr).storageSize());
+}
+
+string StructType::toString(bool _short) const
+{
+ string ret = "struct " + m_struct.name();
+ if (!_short)
+ ret += " " + stringForReferencePart();
+ return ret;
+}
+
+MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
+{
+ MemberList::MemberMap members;
+ for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
+ {
+ TypePointer type = variable->annotation().type;
+ // Skip all mapping members if we are not in storage.
+ if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
+ continue;
+ members.push_back(MemberList::Member(
+ variable->name(),
+ copyForLocationIfReference(type),
+ variable.get())
+ );
+ }
+ return members;
+}
+
+TypePointer StructType::interfaceType(bool _inLibrary) const
+{
+ if (_inLibrary && location() == DataLocation::Storage)
+ return shared_from_this();
+ else
+ return TypePointer();
+}
+
+TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
+{
+ auto copy = make_shared<StructType>(m_struct, _location);
+ copy->m_isPointer = _isPointer;
+ return copy;
+}
+
+string StructType::canonicalName(bool _addDataLocation) const
+{
+ string ret = m_struct.annotation().canonicalName;
+ if (_addDataLocation && location() == DataLocation::Storage)
+ ret += " storage";
+ return ret;
+}
+
+FunctionTypePointer StructType::constructorType() const
+{
+ TypePointers paramTypes;
+ strings paramNames;
+ for (auto const& member: members(nullptr))
+ {
+ if (!member.type->canLiveOutsideStorage())
+ continue;
+ paramNames.push_back(member.name);
+ paramTypes.push_back(copyForLocationIfReference(DataLocation::Memory, member.type));
+ }
+ return make_shared<FunctionType>(
+ paramTypes,
+ TypePointers{copyForLocation(DataLocation::Memory, false)},
+ paramNames,
+ strings(),
+ FunctionType::Location::Internal
+ );
+}
+
+pair<u256, unsigned> const& StructType::storageOffsetsOfMember(string const& _name) const
+{
+ auto const* offsets = members(nullptr).memberStorageOffset(_name);
+ solAssert(offsets, "Storage offset of non-existing member requested.");
+ return *offsets;
+}
+
+u256 StructType::memoryOffsetOfMember(string const& _name) const
+{
+ u256 offset;
+ for (auto const& member: members(nullptr))
+ if (member.name == _name)
+ return offset;
+ else
+ offset += member.type->memoryHeadSize();
+ solAssert(false, "Member not found in struct.");
+ return 0;
+}
+
+set<string> StructType::membersMissingInMemory() const
+{
+ set<string> missing;
+ for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
+ if (!variable->annotation().type->canLiveOutsideStorage())
+ missing.insert(variable->name());
+ return missing;
+}
+
+TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
+}
+
+bool EnumType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ EnumType const& other = dynamic_cast<EnumType const&>(_other);
+ return other.m_enum == m_enum;
+}
+
+unsigned EnumType::storageBytes() const
+{
+ size_t elements = m_enum.members().size();
+ if (elements <= 1)
+ return 1;
+ else
+ return dev::bytesRequired(elements - 1);
+}
+
+string EnumType::toString(bool) const
+{
+ return string("enum ") + m_enum.name();
+}
+
+string EnumType::canonicalName(bool) const
+{
+ return m_enum.annotation().canonicalName;
+}
+
+bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
+}
+
+unsigned int EnumType::memberValue(ASTString const& _member) const
+{
+ unsigned int index = 0;
+ for (ASTPointer<EnumValue> const& decl: m_enum.members())
+ {
+ if (decl->name() == _member)
+ return index;
+ ++index;
+ }
+ BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member));
+}
+
+bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
+{
+ if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
+ {
+ TypePointers const& targets = tupleType->components();
+ if (targets.empty())
+ return components().empty();
+ if (components().size() != targets.size() && !targets.front() && !targets.back())
+ return false; // (,a,) = (1,2,3,4) - unable to position `a` in the tuple.
+ size_t minNumValues = targets.size();
+ if (!targets.back() || !targets.front())
+ --minNumValues; // wildcards can also match 0 components
+ if (components().size() < minNumValues)
+ return false;
+ if (components().size() > targets.size() && targets.front() && targets.back())
+ return false; // larger source and no wildcard
+ bool fillRight = !targets.back() || targets.front();
+ for (size_t i = 0; i < min(targets.size(), components().size()); ++i)
+ {
+ auto const& s = components()[fillRight ? i : components().size() - i - 1];
+ auto const& t = targets[fillRight ? i : targets.size() - i - 1];
+ if (!s && t)
+ return false;
+ else if (s && t && !s->isImplicitlyConvertibleTo(*t))
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+bool TupleType::operator==(Type const& _other) const
+{
+ if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
+ return components() == tupleType->components();
+ else
+ return false;
+}
+
+string TupleType::toString(bool _short) const
+{
+ if (components().empty())
+ return "tuple()";
+ string str = "tuple(";
+ for (auto const& t: components())
+ str += (t ? t->toString(_short) : "") + ",";
+ str.pop_back();
+ return str + ")";
+}
+
+u256 TupleType::storageSize() const
+{
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError() <<
+ errinfo_comment("Storage size of non-storable tuple type requested.")
+ );
+}
+
+unsigned TupleType::sizeOnStack() const
+{
+ unsigned size = 0;
+ for (auto const& t: components())
+ size += t ? t->sizeOnStack() : 0;
+ return size;
+}
+
+TypePointer TupleType::mobileType() const
+{
+ TypePointers mobiles;
+ for (auto const& c: components())
+ mobiles.push_back(c ? c->mobileType() : TypePointer());
+ return make_shared<TupleType>(mobiles);
+}
+
+TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) const
+{
+ solAssert(!!_targetType, "");
+ TypePointers const& targetComponents = dynamic_cast<TupleType const&>(*_targetType).components();
+ bool fillRight = !targetComponents.empty() && (!targetComponents.back() || targetComponents.front());
+ TypePointers tempComponents(targetComponents.size());
+ for (size_t i = 0; i < min(targetComponents.size(), components().size()); ++i)
+ {
+ size_t si = fillRight ? i : components().size() - i - 1;
+ size_t ti = fillRight ? i : targetComponents.size() - i - 1;
+ if (components()[si] && targetComponents[ti])
+ tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]);
+ }
+ return make_shared<TupleType>(tempComponents);
+}
+
+FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
+ m_location(_isInternal ? Location::Internal : Location::External),
+ m_isConstant(_function.isDeclaredConst()),
+ m_declaration(&_function)
+{
+ TypePointers params;
+ vector<string> paramNames;
+ TypePointers retParams;
+ vector<string> retParamNames;
+
+ params.reserve(_function.parameters().size());
+ paramNames.reserve(_function.parameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
+ {
+ paramNames.push_back(var->name());
+ params.push_back(var->annotation().type);
+ }
+ retParams.reserve(_function.returnParameters().size());
+ retParamNames.reserve(_function.returnParameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
+ {
+ retParamNames.push_back(var->name());
+ retParams.push_back(var->annotation().type);
+ }
+ swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
+ swap(retParams, m_returnParameterTypes);
+ swap(retParamNames, m_returnParameterNames);
+}
+
+FunctionType::FunctionType(VariableDeclaration const& _varDecl):
+ m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl)
+{
+ TypePointers paramTypes;
+ vector<string> paramNames;
+ auto returnType = _varDecl.annotation().type;
+
+ while (true)
+ {
+ if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
+ {
+ paramTypes.push_back(mappingType->keyType());
+ paramNames.push_back("");
+ returnType = mappingType->valueType();
+ }
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
+ {
+ if (arrayType->isByteArray())
+ // Return byte arrays as as whole.
+ break;
+ returnType = arrayType->baseType();
+ paramNames.push_back("");
+ paramTypes.push_back(make_shared<IntegerType>(256));
+ }
+ else
+ break;
+ }
+
+ TypePointers retParams;
+ vector<string> retParamNames;
+ if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
+ {
+ for (auto const& member: structType->members(nullptr))
+ if (member.type->category() != Category::Mapping)
+ {
+ if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
+ if (!arrayType->isByteArray())
+ continue;
+ retParams.push_back(member.type);
+ retParamNames.push_back(member.name);
+ }
+ }
+ else
+ {
+ retParams.push_back(ReferenceType::copyForLocationIfReference(
+ DataLocation::Memory,
+ returnType
+ ));
+ retParamNames.push_back("");
+ }
+
+ swap(paramTypes, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
+ swap(retParams, m_returnParameterTypes);
+ swap(retParamNames, m_returnParameterNames);
+}
+
+FunctionType::FunctionType(const EventDefinition& _event):
+ m_location(Location::Event), m_isConstant(true), m_declaration(&_event)
+{
+ TypePointers params;
+ vector<string> paramNames;
+ params.reserve(_event.parameters().size());
+ paramNames.reserve(_event.parameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _event.parameters())
+ {
+ paramNames.push_back(var->name());
+ params.push_back(var->annotation().type);
+ }
+ swap(params, m_parameterTypes);
+ swap(paramNames, m_parameterNames);
+}
+
+vector<string> FunctionType::parameterNames() const
+{
+ if (!bound())
+ return m_parameterNames;
+ return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
+}
+
+TypePointers FunctionType::parameterTypes() const
+{
+ if (!bound())
+ return m_parameterTypes;
+ return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
+}
+
+bool FunctionType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
+
+ if (m_location != other.m_location)
+ return false;
+ if (m_isConstant != other.isConstant())
+ return false;
+
+ if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
+ m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
+ return false;
+ auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
+
+ if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
+ other.m_parameterTypes.cbegin(), typeCompare))
+ return false;
+ if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
+ other.m_returnParameterTypes.cbegin(), typeCompare))
+ return false;
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
+ return false;
+ if (bound() != other.bound())
+ return false;
+ if (bound() && *selfType() != *other.selfType())
+ return false;
+ return true;
+}
+
+string FunctionType::toString(bool _short) const
+{
+ string name = "function (";
+ for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
+ name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
+ name += ") returns (";
+ for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
+ name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
+ return name + ")";
+}
+
+u256 FunctionType::storageSize() const
+{
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError()
+ << errinfo_comment("Storage size of non-storable function type requested."));
+}
+
+unsigned FunctionType::sizeOnStack() const
+{
+ Location location = m_location;
+ if (m_location == Location::SetGas || m_location == Location::SetValue)
+ {
+ solAssert(m_returnParameterTypes.size() == 1, "");
+ location = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_location;
+ }
+
+ unsigned size = 0;
+ if (location == Location::External || location == Location::CallCode || location == Location::DelegateCall)
+ size = 2;
+ else if (location == Location::Bare || location == Location::BareCallCode || location == Location::BareDelegateCall)
+ size = 1;
+ else if (location == Location::Internal)
+ size = 1;
+ else if (location == Location::ArrayPush || location == Location::ByteArrayPush)
+ size = 1;
+ if (m_gasSet)
+ size++;
+ if (m_valueSet)
+ size++;
+ if (bound())
+ size += m_parameterTypes.front()->sizeOnStack();
+ return size;
+}
+
+FunctionTypePointer FunctionType::interfaceFunctionType() const
+{
+ // Note that m_declaration might also be a state variable!
+ solAssert(m_declaration, "Declaration needed to determine interface function type.");
+ bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
+
+ TypePointers paramTypes;
+ TypePointers retParamTypes;
+
+ for (auto type: m_parameterTypes)
+ {
+ if (auto ext = type->interfaceType(isLibraryFunction))
+ paramTypes.push_back(ext);
+ else
+ return FunctionTypePointer();
+ }
+ for (auto type: m_returnParameterTypes)
+ {
+ if (auto ext = type->interfaceType(isLibraryFunction))
+ retParamTypes.push_back(ext);
+ else
+ return FunctionTypePointer();
+ }
+ auto variable = dynamic_cast<VariableDeclaration const*>(m_declaration);
+ if (variable && retParamTypes.empty())
+ return FunctionTypePointer();
+
+ return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters);
+}
+
+MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const
+{
+ switch (m_location)
+ {
+ case Location::External:
+ case Location::Creation:
+ case Location::ECRecover:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ case Location::Bare:
+ case Location::BareCallCode:
+ case Location::BareDelegateCall:
+ {
+ MemberList::MemberMap members;
+ if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall)
+ members.push_back(MemberList::Member(
+ "value",
+ make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ strings(),
+ strings(),
+ Location::SetValue,
+ false,
+ nullptr,
+ m_gasSet,
+ m_valueSet
+ )
+ ));
+ if (m_location != Location::Creation)
+ members.push_back(MemberList::Member(
+ "gas",
+ make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(true, false)},
+ strings(),
+ strings(),
+ Location::SetGas,
+ false,
+ nullptr,
+ m_gasSet,
+ m_valueSet
+ )
+ ));
+ return members;
+ }
+ default:
+ return MemberList::MemberMap();
+ }
+}
+
+bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
+{
+ solAssert(!bound() || _selfType, "");
+ if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
+ return false;
+ TypePointers paramTypes = parameterTypes();
+ if (takesArbitraryParameters())
+ return true;
+ else if (_argumentTypes.size() != paramTypes.size())
+ return false;
+ else
+ return equal(
+ _argumentTypes.cbegin(),
+ _argumentTypes.cend(),
+ paramTypes.cbegin(),
+ [](TypePointer const& argumentType, TypePointer const& parameterType)
+ {
+ return argumentType->isImplicitlyConvertibleTo(*parameterType);
+ }
+ );
+}
+
+bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const
+{
+ if (m_parameterTypes.size() != _other.m_parameterTypes.size())
+ return false;
+ return equal(
+ m_parameterTypes.cbegin(),
+ m_parameterTypes.cend(),
+ _other.m_parameterTypes.cbegin(),
+ [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
+ );
+}
+
+bool FunctionType::isBareCall() const
+{
+ switch (m_location)
+ {
+ case Location::Bare:
+ case Location::BareCallCode:
+ case Location::BareDelegateCall:
+ case Location::ECRecover:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ return true;
+ default:
+ return false;
+ }
+}
+
+string FunctionType::externalSignature() const
+{
+ solAssert(m_declaration != nullptr, "External signature of function needs declaration");
+
+ bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
+
+ string ret = m_declaration->name() + "(";
+
+ FunctionTypePointer external = interfaceFunctionType();
+ solAssert(!!external, "External function type requested.");
+ TypePointers externalParameterTypes = external->parameterTypes();
+ for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
+ {
+ solAssert(!!(*it), "Parameter should have external type");
+ ret += (*it)->canonicalName(_inLibrary) + (it + 1 == externalParameterTypes.cend() ? "" : ",");
+ }
+
+ return ret + ")";
+}
+
+u256 FunctionType::externalIdentifier() const
+{
+ return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature())));
+}
+
+TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
+{
+ TypePointers pointers;
+ pointers.reserve(_types.size());
+ for (string const& type: _types)
+ pointers.push_back(Type::fromElementaryTypeName(type));
+ return pointers;
+}
+
+TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
+{
+ return make_shared<FunctionType>(
+ m_parameterTypes,
+ m_returnParameterTypes,
+ m_parameterNames,
+ m_returnParameterNames,
+ m_location,
+ m_arbitraryParameters,
+ m_declaration,
+ m_gasSet || _setGas,
+ m_valueSet || _setValue,
+ m_bound
+ );
+}
+
+FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const
+{
+ TypePointers parameterTypes;
+ for (auto const& t: m_parameterTypes)
+ {
+ auto refType = dynamic_cast<ReferenceType const*>(t.get());
+ if (refType && refType->location() == DataLocation::CallData)
+ parameterTypes.push_back(refType->copyForLocation(DataLocation::Memory, false));
+ else
+ parameterTypes.push_back(t);
+ }
+
+ // Removes dynamic types.
+ TypePointers returnParameterTypes;
+ vector<string> returnParameterNames;
+ for (size_t i = 0; i < m_returnParameterTypes.size(); ++i)
+ if (!m_returnParameterTypes[i]->isDynamicallySized())
+ {
+ returnParameterTypes.push_back(m_returnParameterTypes[i]);
+ returnParameterNames.push_back(m_returnParameterNames[i]);
+ }
+ return make_shared<FunctionType>(
+ parameterTypes,
+ returnParameterTypes,
+ m_parameterNames,
+ returnParameterNames,
+ _inLibrary ? Location::DelegateCall : m_location,
+ m_arbitraryParameters,
+ m_declaration,
+ m_gasSet,
+ m_valueSet,
+ _bound
+ );
+}
+
+vector<string> const FunctionType::parameterTypeNames(bool _addDataLocation) const
+{
+ vector<string> names;
+ for (TypePointer const& t: parameterTypes())
+ names.push_back(t->canonicalName(_addDataLocation));
+
+ return names;
+}
+
+vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocation) const
+{
+ vector<string> names;
+ for (TypePointer const& t: m_returnParameterTypes)
+ names.push_back(t->canonicalName(_addDataLocation));
+
+ return names;
+}
+
+TypePointer FunctionType::selfType() const
+{
+ solAssert(bound(), "");
+ return m_parameterTypes.at(0);
+}
+
+ASTPointer<ASTString> FunctionType::documentation() const
+{
+ auto function = dynamic_cast<Documented const*>(m_declaration);
+ if (function)
+ return function->documentation();
+
+ return ASTPointer<ASTString>();
+}
+
+bool MappingType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ MappingType const& other = dynamic_cast<MappingType const&>(_other);
+ return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
+}
+
+string MappingType::toString(bool _short) const
+{
+ return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")";
+}
+
+string MappingType::canonicalName(bool) const
+{
+ return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")";
+}
+
+bool TypeType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ TypeType const& other = dynamic_cast<TypeType const&>(_other);
+ return *actualType() == *other.actualType();
+}
+
+u256 TypeType::storageSize() const
+{
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError()
+ << errinfo_comment("Storage size of non-storable type type requested."));
+}
+
+unsigned TypeType::sizeOnStack() const
+{
+ if (auto contractType = dynamic_cast<ContractType const*>(m_actualType.get()))
+ if (contractType->contractDefinition().isLibrary())
+ return 1;
+ return 0;
+}
+
+MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const
+{
+ MemberList::MemberMap members;
+ if (m_actualType->category() == Category::Contract)
+ {
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
+ bool isBase = false;
+ if (_currentScope != nullptr)
+ {
+ auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
+ isBase = (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end());
+ }
+ if (contract.isLibrary())
+ for (auto const& it: contract.interfaceFunctions())
+ members.push_back(MemberList::Member(
+ it.second->declaration().name(),
+ it.second->asMemberFunction(true),
+ &it.second->declaration()
+ ));
+ if (isBase)
+ {
+ // We are accessing the type of a base contract, so add all public and protected
+ // members. Note that this does not add inherited functions on purpose.
+ for (Declaration const* decl: contract.inheritableMembers())
+ members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
+ }
+ else
+ {
+ for (auto const& stru: contract.definedStructs())
+ members.push_back(MemberList::Member(stru->name(), stru->type(), stru));
+ for (auto const& enu: contract.definedEnums())
+ members.push_back(MemberList::Member(enu->name(), enu->type(), enu));
+ }
+ }
+ else if (m_actualType->category() == Category::Enum)
+ {
+ EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
+ auto enumType = make_shared<EnumType>(enumDef);
+ for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
+ members.push_back(MemberList::Member(enumValue->name(), enumType));
+ }
+ return members;
+}
+
+ModifierType::ModifierType(const ModifierDefinition& _modifier)
+{
+ TypePointers params;
+ params.reserve(_modifier.parameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _modifier.parameters())
+ params.push_back(var->annotation().type);
+ swap(params, m_parameterTypes);
+}
+
+u256 ModifierType::storageSize() const
+{
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError()
+ << errinfo_comment("Storage size of non-storable type type requested."));
+}
+
+bool ModifierType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ ModifierType const& other = dynamic_cast<ModifierType const&>(_other);
+
+ if (m_parameterTypes.size() != other.m_parameterTypes.size())
+ return false;
+ auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
+
+ if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
+ other.m_parameterTypes.cbegin(), typeCompare))
+ return false;
+ return true;
+}
+
+string ModifierType::toString(bool _short) const
+{
+ string name = "modifier (";
+ for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
+ name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
+ return name + ")";
+}
+
+bool ModuleType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
+}
+
+MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
+{
+ MemberList::MemberMap symbols;
+ for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
+ for (Declaration const* symbol: symbolName.second)
+ symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
+ return symbols;
+}
+
+string ModuleType::toString(bool) const
+{
+ return string("module \"") + m_sourceUnit.annotation().path + string("\"");
+}
+
+bool MagicType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ MagicType const& other = dynamic_cast<MagicType const&>(_other);
+ return other.m_kind == m_kind;
+}
+
+MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
+{
+ switch (m_kind)
+ {
+ case Kind::Block:
+ return MemberList::MemberMap({
+ {"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"timestamp", make_shared<IntegerType>(256)},
+ {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)},
+ {"difficulty", make_shared<IntegerType>(256)},
+ {"number", make_shared<IntegerType>(256)},
+ {"gaslimit", make_shared<IntegerType>(256)}
+ });
+ case Kind::Message:
+ return MemberList::MemberMap({
+ {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"gas", make_shared<IntegerType>(256)},
+ {"value", make_shared<IntegerType>(256)},
+ {"data", make_shared<ArrayType>(DataLocation::CallData)},
+ {"sig", make_shared<FixedBytesType>(4)}
+ });
+ case Kind::Transaction:
+ return MemberList::MemberMap({
+ {"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
+ {"gasprice", make_shared<IntegerType>(256)}
+ });
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
+ }
+}
+
+string MagicType::toString(bool) const
+{
+ switch (m_kind)
+ {
+ case Kind::Block:
+ return "block";
+ case Kind::Message:
+ return "msg";
+ case Kind::Transaction:
+ return "tx";
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
+ }
+}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
new file mode 100644
index 00000000..1d65aeb6
--- /dev/null
+++ b/libsolidity/ast/Types.h
@@ -0,0 +1,1033 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity data types
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <map>
+#include <boost/noncopyable.hpp>
+#include <libdevcore/Common.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/parsing/Token.h>
+#include <libdevcore/UndefMacros.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Type; // forward
+class FunctionType; // forward
+using TypePointer = std::shared_ptr<Type const>;
+using FunctionTypePointer = std::shared_ptr<FunctionType const>;
+using TypePointers = std::vector<TypePointer>;
+
+
+enum class DataLocation { Storage, CallData, Memory };
+
+/**
+ * Helper class to compute storage offsets of members of structs and contracts.
+ */
+class StorageOffsets
+{
+public:
+ /// Resets the StorageOffsets objects and determines the position in storage for each
+ /// of the elements of @a _types.
+ void computeOffsets(TypePointers const& _types);
+ /// @returns the offset of the given member, might be null if the member is not part of storage.
+ std::pair<u256, unsigned> const* offset(size_t _index) const;
+ /// @returns the total number of slots occupied by all members.
+ u256 const& storageSize() const { return m_storageSize; }
+
+private:
+ u256 m_storageSize;
+ std::map<size_t, std::pair<u256, unsigned>> m_offsets;
+};
+
+/**
+ * List of members of a type.
+ */
+class MemberList
+{
+public:
+ struct Member
+ {
+ Member(std::string const& _name, TypePointer const& _type, Declaration const* _declaration = nullptr):
+ name(_name),
+ type(_type),
+ declaration(_declaration)
+ {
+ }
+
+ std::string name;
+ TypePointer type;
+ Declaration const* declaration = nullptr;
+ };
+
+ using MemberMap = std::vector<Member>;
+
+ MemberList() {}
+ explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
+ MemberList& operator=(MemberList&& _other);
+ void combine(MemberList const& _other);
+ TypePointer memberType(std::string const& _name) const
+ {
+ TypePointer type;
+ for (auto const& it: m_memberTypes)
+ if (it.name == _name)
+ {
+ solAssert(!type, "Requested member type by non-unique name.");
+ type = it.type;
+ }
+ return type;
+ }
+ MemberMap membersByName(std::string const& _name) const
+ {
+ MemberMap members;
+ for (auto const& it: m_memberTypes)
+ if (it.name == _name)
+ members.push_back(it);
+ return members;
+ }
+ /// @returns the offset of the given member in storage slots and bytes inside a slot or
+ /// a nullptr if the member is not part of storage.
+ std::pair<u256, unsigned> const* memberStorageOffset(std::string const& _name) const;
+ /// @returns the number of storage slots occupied by the members.
+ u256 const& storageSize() const;
+
+ MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
+ MemberMap::const_iterator end() const { return m_memberTypes.end(); }
+
+private:
+ MemberMap m_memberTypes;
+ mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
+};
+
+/**
+ * Abstract base class that forms the root of the type hierarchy.
+ */
+class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
+{
+public:
+ enum class Category
+ {
+ Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
+ FixedBytes, Contract, Struct, Function, Enum, Tuple,
+ Mapping, TypeType, Modifier, Magic, Module
+ };
+
+ /// @{
+ /// @name Factory functions
+ /// Factory functions that convert an AST @ref TypeName to a Type.
+ static TypePointer fromElementaryTypeName(ElementaryTypeNameToken const& _type);
+ static TypePointer fromElementaryTypeName(std::string const& _name);
+ /// @}
+
+ /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
+ /// not fit any type.
+ static TypePointer forLiteral(Literal const& _literal);
+ /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
+ static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
+
+ virtual Category category() const = 0;
+ virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
+ {
+ return isImplicitlyConvertibleTo(_convertTo);
+ }
+ /// @returns the resulting type of applying the given unary operator or an empty pointer if
+ /// this is not possible.
+ /// The default implementation does not allow any unary operator.
+ virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
+ /// @returns the resulting type of applying the given binary operator or an empty pointer if
+ /// this is not possible.
+ /// The default implementation allows comparison operators if a common type exists
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+ {
+ return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
+ virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
+
+ /// @returns number of bytes used by this type when encoded for CALL. If it is a dynamic type,
+ /// returns the size of the pointer (usually 32). Returns 0 if the type cannot be encoded
+ /// in calldata.
+ /// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes.
+ virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; }
+ /// @returns the size of this data type in bytes when stored in memory. For memory-reference
+ /// types, this is the size of the memory pointer.
+ virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); }
+ /// Convenience version of @see calldataEncodedSize(bool)
+ unsigned calldataEncodedSize() const { return calldataEncodedSize(true); }
+ /// @returns true if the type is dynamically encoded in calldata
+ virtual bool isDynamicallySized() const { return false; }
+ /// @returns the number of storage slots required to hold this value in storage.
+ /// For dynamically "allocated" types, it returns the size of the statically allocated head,
+ virtual u256 storageSize() const { return 1; }
+ /// Multiple small types can be packed into a single storage slot. If such a packing is possible
+ /// this function @returns the size in bytes smaller than 32. Data is moved to the next slot if
+ /// it does not fit.
+ /// In order to avoid computation at runtime of whether such moving is necessary, structs and
+ /// array data (not each element) always start a new slot.
+ virtual unsigned storageBytes() const { return 32; }
+ /// Returns true if the type can be stored in storage.
+ virtual bool canBeStored() const { return true; }
+ /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
+ virtual bool canLiveOutsideStorage() const { return true; }
+ /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
+ /// i.e. it behaves differently in lvalue context and in value context.
+ virtual bool isValueType() const { return false; }
+ virtual unsigned sizeOnStack() const { return 1; }
+ /// @returns the mobile (in contrast to static) type corresponding to the given type.
+ /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type
+ /// for storage reference types.
+ virtual TypePointer mobileType() const { return shared_from_this(); }
+ /// @returns true if this is a non-value type and the data of this type is stored at the
+ /// given location.
+ virtual bool dataStoredIn(DataLocation) const { return false; }
+ /// @returns the type of a temporary during assignment to a variable of the given type.
+ /// Specifically, returns the requested itself if it can be dynamically allocated (or is a value type)
+ /// and the mobile type otherwise.
+ virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const
+ {
+ return _targetType->dataStoredIn(DataLocation::Storage) ? mobileType() : _targetType;
+ }
+
+ /// Returns the list of all members of this type. Default implementation: no members apart from bound.
+ /// @param _currentScope scope in which the members are accessed.
+ MemberList const& members(ContractDefinition const* _currentScope) const;
+ /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
+ TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const
+ {
+ return members(_currentScope).memberType(_name);
+ }
+
+ virtual std::string toString(bool _short) const = 0;
+ std::string toString() const { return toString(false); }
+ /// @returns the canonical name of this type for use in function signatures.
+ /// @param _addDataLocation if true, includes data location for reference types if it is "storage".
+ virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); }
+ virtual u256 literalValue(Literal const*) const
+ {
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError() <<
+ errinfo_comment("Literal value requested for type without literals.")
+ );
+ }
+
+ /// @returns a (simpler) type that is encoded in the same way for external function calls.
+ /// This for example returns address for contract types.
+ /// If there is no such type, returns an empty shared pointer.
+ virtual TypePointer encodingType() const { return TypePointer(); }
+ /// @returns a (simpler) type that is used when decoding this type in calldata.
+ virtual TypePointer decodingType() const { return encodingType(); }
+ /// @returns a type that will be used outside of Solidity for e.g. function signatures.
+ /// This for example returns address for contract types.
+ /// If there is no such type, returns an empty shared pointer.
+ /// @param _inLibrary if set, returns types as used in a library, e.g. struct and contract types
+ /// are returned without modification.
+ virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); }
+
+private:
+ /// @returns a member list containing all members added to this type by `using for` directives.
+ static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope);
+
+protected:
+ /// @returns the members native to this type depending on the given context. This function
+ /// is used (in conjunction with boundFunctions to fill m_members below.
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const
+ {
+ return MemberList::MemberMap();
+ }
+
+ /// List of member types (parameterised by scape), will be lazy-initialized.
+ mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
+};
+
+/**
+ * Any kind of integer type (signed, unsigned, address).
+ */
+class IntegerType: public Type
+{
+public:
+ enum class Modifier
+ {
+ Unsigned, Signed, Address
+ };
+ virtual Category category() const override { return Category::Integer; }
+
+ explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+
+ virtual bool operator==(Type const& _other) const override;
+
+ virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; }
+ virtual unsigned storageBytes() const override { return m_bits / 8; }
+ virtual bool isValueType() const override { return true; }
+
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+
+ virtual std::string toString(bool _short) const override;
+
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
+
+ int numBits() const { return m_bits; }
+ bool isAddress() const { return m_modifier == Modifier::Address; }
+ bool isSigned() const { return m_modifier == Modifier::Signed; }
+
+private:
+ int m_bits;
+ Modifier m_modifier;
+};
+
+/**
+ * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10.
+ * There is one distinct type per value.
+ */
+class IntegerConstantType: public Type
+{
+public:
+ virtual Category category() const override { return Category::IntegerConstant; }
+
+ /// @returns true if the literal is a valid integer.
+ static bool isValidLiteral(Literal const& _literal);
+
+ explicit IntegerConstantType(Literal const& _literal);
+ explicit IntegerConstantType(bigint _value): m_value(_value) {}
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+
+ virtual bool operator==(Type const& _other) const override;
+
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return false; }
+
+ virtual std::string toString(bool _short) const override;
+ virtual u256 literalValue(Literal const* _literal) const override;
+ virtual TypePointer mobileType() const override;
+
+ /// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
+ std::shared_ptr<IntegerType const> integerType() const;
+
+private:
+ bigint m_value;
+};
+
+/**
+ * Literal string, can be converted to bytes, bytesX or string.
+ */
+class StringLiteralType: public Type
+{
+public:
+ virtual Category category() const override { return Category::StringLiteral; }
+
+ explicit StringLiteralType(Literal const& _literal);
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const override;
+
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override { return 0; }
+
+ virtual std::string toString(bool) const override { return "literal_string \"" + m_value + "\""; }
+ virtual TypePointer mobileType() const override;
+
+ std::string const& value() const { return m_value; }
+
+private:
+ std::string m_value;
+};
+
+/**
+ * Bytes type with fixed length of up to 32 bytes.
+ */
+class FixedBytesType: public Type
+{
+public:
+ virtual Category category() const override { return Category::FixedBytes; }
+
+ /// @returns the smallest bytes type for the given literal or an empty pointer
+ /// if no type fits.
+ static std::shared_ptr<FixedBytesType> smallestTypeForLiteral(std::string const& _literal);
+
+ explicit FixedBytesType(int _bytes);
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+
+ virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
+ virtual unsigned storageBytes() const override { return m_bytes; }
+ virtual bool isValueType() const override { return true; }
+
+ virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
+
+ int numBytes() const { return m_bytes; }
+
+private:
+ int m_bytes;
+};
+
+/**
+ * The boolean type.
+ */
+class BoolType: public Type
+{
+public:
+ BoolType() {}
+ virtual Category category() const override { return Category::Bool; }
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+
+ virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
+ virtual unsigned storageBytes() const override { return 1; }
+ virtual bool isValueType() const override { return true; }
+
+ virtual std::string toString(bool) const override { return "bool"; }
+ virtual u256 literalValue(Literal const* _literal) const override;
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
+};
+
+/**
+ * Base class used by types which are not value types and can be stored either in storage, memory
+ * or calldata. This is currently used by arrays and structs.
+ */
+class ReferenceType: public Type
+{
+public:
+ explicit ReferenceType(DataLocation _location): m_location(_location) {}
+ DataLocation location() const { return m_location; }
+
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+ virtual unsigned memoryHeadSize() const override { return 32; }
+
+ /// @returns a copy of this type with location (recursively) changed to @a _location,
+ /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference.
+ virtual TypePointer copyForLocation(DataLocation _location, bool _isPointer) const = 0;
+
+ virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); }
+ virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; }
+
+ /// Storage references can be pointers or bound references. In general, local variables are of
+ /// pointer type, state variables are bound references. Assignments to pointers or deleting
+ /// them will not modify storage (that will only change the pointer). Assignment from
+ /// non-storage objects to a variable of storage pointer type is not possible.
+ bool isPointer() const { return m_isPointer; }
+
+ bool operator==(ReferenceType const& _other) const
+ {
+ return location() == _other.location() && isPointer() == _other.isPointer();
+ }
+
+ /// @returns a copy of @a _type having the same location as this (and is not a pointer type)
+ /// if _type is a reference type and an unmodified copy of _type otherwise.
+ /// This function is mostly useful to modify inner types appropriately.
+ static TypePointer copyForLocationIfReference(DataLocation _location, TypePointer const& _type);
+
+protected:
+ TypePointer copyForLocationIfReference(TypePointer const& _type) const;
+ /// @returns a human-readable description of the reference part of the type.
+ std::string stringForReferencePart() const;
+
+ DataLocation m_location = DataLocation::Storage;
+ bool m_isPointer = true;
+};
+
+/**
+ * The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
+ * and dynamically-sized array (<type>[]).
+ * In storage, all arrays are packed tightly (as long as more than one elementary type fits in
+ * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
+ * thus start on their own slot.
+ */
+class ArrayType: public ReferenceType
+{
+public:
+ virtual Category category() const override { return Category::Array; }
+
+ /// Constructor for a byte array ("bytes") and string.
+ explicit ArrayType(DataLocation _location, bool _isString = false):
+ ReferenceType(_location),
+ m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
+ m_baseType(std::make_shared<FixedBytesType>(1))
+ {
+ }
+ /// Constructor for a dynamically sized array type ("type[]")
+ ArrayType(DataLocation _location, TypePointer const& _baseType):
+ ReferenceType(_location),
+ m_baseType(copyForLocationIfReference(_baseType))
+ {
+ }
+ /// Constructor for a fixed-size array type ("type[20]")
+ ArrayType(DataLocation _location, TypePointer const& _baseType, u256 const& _length):
+ ReferenceType(_location),
+ m_baseType(copyForLocationIfReference(_baseType)),
+ m_hasDynamicLength(false),
+ m_length(_length)
+ {}
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual bool operator==(const Type& _other) const override;
+ virtual unsigned calldataEncodedSize(bool _padded) const override;
+ virtual bool isDynamicallySized() const override { return m_hasDynamicLength; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
+ virtual unsigned sizeOnStack() const override;
+ virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+ virtual TypePointer encodingType() const override;
+ virtual TypePointer decodingType() const override;
+ virtual TypePointer interfaceType(bool _inLibrary) const override;
+
+ /// @returns true if this is a byte array or a string
+ bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
+ /// @returns true if this is a string
+ bool isString() const { return m_arrayKind == ArrayKind::String; }
+ TypePointer const& baseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
+ u256 const& length() const { return m_length; }
+ u256 memorySize() const;
+
+ TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
+
+private:
+ /// String is interpreted as a subtype of Bytes.
+ enum class ArrayKind { Ordinary, Bytes, String };
+
+ ///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
+ ArrayKind m_arrayKind = ArrayKind::Ordinary;
+ TypePointer m_baseType;
+ bool m_hasDynamicLength = true;
+ u256 m_length;
+};
+
+/**
+ * The type of a contract instance or library, there is one distinct type for each contract definition.
+ */
+class ContractType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Contract; }
+ explicit ContractType(ContractDefinition const& _contract, bool _super = false):
+ m_contract(_contract), m_super(_super) {}
+ /// Contracts can be implicitly converted to super classes and to addresses.
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ /// Contracts can be converted to themselves and to integers.
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual unsigned calldataEncodedSize(bool _padded ) const override
+ {
+ return encodingType()->calldataEncodedSize(_padded);
+ }
+ virtual unsigned storageBytes() const override { return 20; }
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual bool isValueType() const override { return true; }
+ virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+ virtual TypePointer encodingType() const override
+ {
+ return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : encodingType();
+ }
+
+ bool isSuper() const { return m_super; }
+ ContractDefinition const& contractDefinition() const { return m_contract; }
+
+ /// Returns the function type of the constructor. Note that the location part of the function type
+ /// is not used, as this type cannot be the type of a variable or expression.
+ FunctionTypePointer const& constructorType() const;
+
+ /// @returns the identifier of the function with the given name or Invalid256 if such a name does
+ /// not exist.
+ u256 functionIdentifier(std::string const& _functionName) const;
+
+ /// @returns a list of all state variables (including inherited) of the contract and their
+ /// offsets in storage.
+ std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
+
+private:
+ ContractDefinition const& m_contract;
+ /// If true, it is the "super" type of the current contract, i.e. it contains only inherited
+ /// members.
+ bool m_super = false;
+ /// Type of the constructor, @see constructorType. Lazily initialized.
+ mutable FunctionTypePointer m_constructorType;
+};
+
+/**
+ * The type of a struct instance, there is one distinct type per struct definition.
+ */
+class StructType: public ReferenceType
+{
+public:
+ virtual Category category() const override { return Category::Struct; }
+ explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
+ ReferenceType(_location), m_struct(_struct) {}
+ virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual unsigned calldataEncodedSize(bool _padded) const override;
+ u256 memorySize() const;
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual std::string toString(bool _short) const override;
+
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+ virtual TypePointer encodingType() const override
+ {
+ return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override;
+
+ TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
+
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+
+ /// @returns a function that peforms the type conversion between a list of struct members
+ /// and a memory struct of this type.
+ FunctionTypePointer constructorType() const;
+
+ std::pair<u256, unsigned> const& storageOffsetsOfMember(std::string const& _name) const;
+ u256 memoryOffsetOfMember(std::string const& _name) const;
+
+ StructDefinition const& structDefinition() const { return m_struct; }
+
+ /// @returns the set of all members that are removed in the memory version (typically mappings).
+ std::set<std::string> membersMissingInMemory() const;
+
+private:
+ StructDefinition const& m_struct;
+};
+
+/**
+ * The type of an enum instance, there is one distinct type per enum definition.
+ */
+class EnumType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Enum; }
+ explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
+ virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual unsigned calldataEncodedSize(bool _padded) const override
+ {
+ return encodingType()->calldataEncodedSize(_padded);
+ }
+ virtual unsigned storageBytes() const override;
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+ virtual bool isValueType() const override { return true; }
+
+ virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer encodingType() const override
+ {
+ return std::make_shared<IntegerType>(8 * int(storageBytes()));
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : encodingType();
+ }
+
+ EnumDefinition const& enumDefinition() const { return m_enum; }
+ /// @returns the value that the string has in the Enum
+ unsigned int memberValue(ASTString const& _member) const;
+
+private:
+ EnumDefinition const& m_enum;
+};
+
+/**
+ * Type that can hold a finite sequence of values of different types.
+ * In some cases, the components are empty pointers (when used as placeholders).
+ */
+class TupleType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Tuple; }
+ explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
+ virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
+ virtual bool operator==(Type const& _other) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual std::string toString(bool) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override;
+ virtual TypePointer mobileType() const override;
+ /// Converts components to their temporary types and performs some wildcard matching.
+ virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override;
+
+ std::vector<TypePointer> const& components() const { return m_components; }
+
+private:
+ std::vector<TypePointer> const m_components;
+};
+
+/**
+ * The type of a function, identified by its (return) parameter types.
+ * @todo the return parameters should also have names, i.e. return parameters should be a struct
+ * type.
+ */
+class FunctionType: public Type
+{
+public:
+ /// How this function is invoked on the EVM.
+ /// @todo This documentation is outdated, and Location should rather be named "Type"
+ enum class Location
+ {
+ Internal, ///< stack-call using plain JUMP
+ External, ///< external call using CALL
+ CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage
+ DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage
+ Bare, ///< CALL without function hash
+ BareCallCode, ///< CALLCODE without function hash
+ BareDelegateCall, ///< DELEGATECALL without function hash
+ Creation, ///< external call using CREATE
+ Send, ///< CALL, but without data and gas
+ SHA3, ///< SHA3
+ Selfdestruct, ///< SELFDESTRUCT
+ ECRecover, ///< CALL to special contract for ecrecover
+ SHA256, ///< CALL to special contract for sha256
+ RIPEMD160, ///< CALL to special contract for ripemd160
+ Log0,
+ Log1,
+ Log2,
+ Log3,
+ Log4,
+ Event, ///< syntactic sugar for LOG*
+ SetGas, ///< modify the default gas value for the function call
+ SetValue, ///< modify the default value transfer for the function call
+ BlockHash, ///< BLOCKHASH
+ AddMod, ///< ADDMOD
+ MulMod, ///< MULMOD
+ ArrayPush, ///< .push() to a dynamically sized array in storage
+ ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
+ ObjectCreation ///< array creation using new
+ };
+
+ virtual Category category() const override { return Category::Function; }
+
+ /// Creates the type of a function.
+ explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
+ /// Creates the accessor function type of a state variable.
+ explicit FunctionType(VariableDeclaration const& _varDecl);
+ /// Creates the function type of an event.
+ explicit FunctionType(EventDefinition const& _event);
+ FunctionType(
+ strings const& _parameterTypes,
+ strings const& _returnParameterTypes,
+ Location _location = Location::Internal,
+ bool _arbitraryParameters = false
+ ): FunctionType(
+ parseElementaryTypeVector(_parameterTypes),
+ parseElementaryTypeVector(_returnParameterTypes),
+ strings(),
+ strings(),
+ _location,
+ _arbitraryParameters
+ )
+ {
+ }
+ FunctionType(
+ TypePointers const& _parameterTypes,
+ TypePointers const& _returnParameterTypes,
+ strings _parameterNames = strings(),
+ strings _returnParameterNames = strings(),
+ Location _location = Location::Internal,
+ bool _arbitraryParameters = false,
+ Declaration const* _declaration = nullptr,
+ bool _gasSet = false,
+ bool _valueSet = false,
+ bool _bound = false
+ ):
+ m_parameterTypes(_parameterTypes),
+ m_returnParameterTypes(_returnParameterTypes),
+ m_parameterNames(_parameterNames),
+ m_returnParameterNames(_returnParameterNames),
+ m_location(_location),
+ m_arbitraryParameters(_arbitraryParameters),
+ m_gasSet(_gasSet),
+ m_valueSet(_valueSet),
+ m_bound(_bound),
+ m_declaration(_declaration)
+ {}
+
+ TypePointers parameterTypes() const;
+ std::vector<std::string> parameterNames() const;
+ std::vector<std::string> const parameterTypeNames(bool _addDataLocation) const;
+ TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
+ std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; }
+ std::vector<std::string> const returnParameterTypeNames(bool _addDataLocation) const;
+ /// @returns the "self" parameter type for a bound function
+ TypePointer selfType() const;
+
+ virtual bool operator==(Type const& _other) const override;
+ virtual std::string toString(bool _short) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+
+ /// @returns TypePointer of a new FunctionType object. All input/return parameters are an
+ /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
+ /// current function.
+ /// @returns an empty shared pointer if one of the input/return parameters does not have an
+ /// external type.
+ FunctionTypePointer interfaceFunctionType() const;
+
+ /// @returns true if this function can take the given argument types (possibly
+ /// after implicit conversion).
+ /// @param _selfType if the function is bound, this has to be supplied and is the type of the
+ /// expression the function is called on.
+ bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
+ /// @returns true if the types of parameters are equal (does't check return parameter types)
+ bool hasEqualArgumentTypes(FunctionType const& _other) const;
+
+ /// @returns true if the ABI is used for this call (only meaningful for external calls)
+ bool isBareCall() const;
+ Location const& location() const { return m_location; }
+ /// @returns the external signature of this function type given the function name
+ std::string externalSignature() const;
+ /// @returns the external identifier of this function (the hash of the signature).
+ u256 externalIdentifier() const;
+ Declaration const& declaration() const
+ {
+ solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
+ return *m_declaration;
+ }
+ bool hasDeclaration() const { return !!m_declaration; }
+ bool isConstant() const { return m_isConstant; }
+ /// @return A shared pointer of an ASTString.
+ /// Can contain a nullptr in which case indicates absence of documentation
+ ASTPointer<ASTString> documentation() const;
+
+ /// true iff arguments are to be padded to multiples of 32 bytes for external calls
+ bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); }
+ bool takesArbitraryParameters() const { return m_arbitraryParameters; }
+ bool gasSet() const { return m_gasSet; }
+ bool valueSet() const { return m_valueSet; }
+ bool bound() const { return m_bound; }
+
+ /// @returns a copy of this type, where gas or value are set manually. This will never set one
+ /// of the parameters to fals.
+ TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
+
+ /// @returns a copy of this function type where all return parameters of dynamic size are
+ /// removed and the location of reference types is changed from CallData to Memory.
+ /// This is needed if external functions are called on other contracts, as they cannot return
+ /// dynamic values.
+ /// @param _inLibrary if true, uses DelegateCall as location.
+ /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`.
+ FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const;
+
+private:
+ static TypePointers parseElementaryTypeVector(strings const& _types);
+
+ TypePointers m_parameterTypes;
+ TypePointers m_returnParameterTypes;
+ std::vector<std::string> m_parameterNames;
+ std::vector<std::string> m_returnParameterNames;
+ Location const m_location;
+ /// true if the function takes an arbitrary number of arguments of arbitrary types
+ bool const m_arbitraryParameters = false;
+ bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
+ bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
+ bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
+ bool m_isConstant = false;
+ Declaration const* m_declaration = nullptr;
+};
+
+/**
+ * The type of a mapping, there is one distinct type per key/value type pair.
+ * Mappings always occupy their own storage slot, but do not actually use it.
+ */
+class MappingType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Mapping; }
+ MappingType(TypePointer const& _keyType, TypePointer const& _valueType):
+ m_keyType(_keyType), m_valueType(_valueType) {}
+
+ virtual bool operator==(Type const& _other) const override;
+ virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual TypePointer encodingType() const override
+ {
+ return std::make_shared<IntegerType>(256);
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : TypePointer();
+ }
+
+ TypePointer const& keyType() const { return m_keyType; }
+ TypePointer const& valueType() const { return m_valueType; }
+
+private:
+ TypePointer m_keyType;
+ TypePointer m_valueType;
+};
+
+/**
+ * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
+ * of a TypeType.
+ * For super contracts or libraries, this has members directly.
+ */
+class TypeType: public Type
+{
+public:
+ virtual Category category() const override { return Category::TypeType; }
+ explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
+ TypePointer const& actualType() const { return m_actualType; }
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual bool operator==(Type const& _other) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override;
+ virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
+
+private:
+ TypePointer m_actualType;
+};
+
+
+/**
+ * The type of a function modifier. Not used for anything for now.
+ */
+class ModifierType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Modifier; }
+ explicit ModifierType(ModifierDefinition const& _modifier);
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 storageSize() const override;
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned sizeOnStack() const override { return 0; }
+ virtual bool operator==(Type const& _other) const override;
+ virtual std::string toString(bool _short) const override;
+
+private:
+ TypePointers m_parameterTypes;
+};
+
+
+
+/**
+ * Special type for imported modules. These mainly give access to their scope via members.
+ */
+class ModuleType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Module; }
+
+ explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual unsigned sizeOnStack() const override { return 0; }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+
+ virtual std::string toString(bool _short) const override;
+
+private:
+ SourceUnit const& m_sourceUnit;
+};
+
+/**
+ * Special type for magic variables (block, msg, tx), similar to a struct but without any reference
+ * (it always references a global singleton by name).
+ */
+class MagicType: public Type
+{
+public:
+ enum class Kind { Block, Message, Transaction };
+ virtual Category category() const override { return Category::Magic; }
+
+ explicit MagicType(Kind _kind): m_kind(_kind) {}
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual unsigned sizeOnStack() const override { return 0; }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+
+ virtual std::string toString(bool _short) const override;
+
+private:
+ Kind m_kind;
+};
+
+}
+}
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
new file mode 100644
index 00000000..f1acce31
--- /dev/null
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -0,0 +1,968 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Code generation utils that handle arrays.
+ */
+
+#include <libsolidity/codegen/ArrayUtils.h>
+#include <libevmcore/Instruction.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/codegen/LValue.h>
+
+using namespace std;
+using namespace dev;
+using namespace solidity;
+
+void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const
+{
+ // this copies source to target and also clears target if it was larger
+ // need to leave "target_ref target_byte_off" on the stack at the end
+
+ // stack layout: [source_ref] [source length] target_ref (top)
+ solAssert(_targetType.location() == DataLocation::Storage, "");
+
+ IntegerType uint256(256);
+ Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.baseType());
+ Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.baseType());
+
+ // TODO unroll loop for small sizes
+
+ bool sourceIsStorage = _sourceType.location() == DataLocation::Storage;
+ bool fromCalldata = _sourceType.location() == DataLocation::CallData;
+ bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
+ bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->storageBytes() <= 16;
+ bool haveByteOffsetTarget = !directCopy && targetBaseType->storageBytes() <= 16;
+ unsigned byteOffsetSize = (haveByteOffsetSource ? 1 : 0) + (haveByteOffsetTarget ? 1 : 0);
+
+ // stack: source_ref [source_length] target_ref
+ // store target_ref
+ for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i)
+ m_context << eth::swapInstruction(i);
+ // stack: target_ref source_ref [source_length]
+ // stack: target_ref source_ref [source_length]
+ // retrieve source length
+ if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized())
+ retrieveLength(_sourceType); // otherwise, length is already there
+ if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized())
+ {
+ // increment source pointer to point to data
+ m_context << eth::Instruction::SWAP1 << u256(0x20);
+ m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
+ }
+
+ // stack: target_ref source_ref source_length
+ m_context << eth::Instruction::DUP3;
+ // stack: target_ref source_ref source_length target_ref
+ retrieveLength(_targetType);
+ // stack: target_ref source_ref source_length target_ref target_length
+ if (_targetType.isDynamicallySized())
+ // store new target length
+ if (!_targetType.isByteArray())
+ // Otherwise, length will be stored below.
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
+ if (sourceBaseType->category() == Type::Category::Mapping)
+ {
+ solAssert(targetBaseType->category() == Type::Category::Mapping, "");
+ solAssert(_sourceType.location() == DataLocation::Storage, "");
+ // nothing to copy
+ m_context
+ << eth::Instruction::POP << eth::Instruction::POP
+ << eth::Instruction::POP << eth::Instruction::POP;
+ return;
+ }
+ // stack: target_ref source_ref source_length target_ref target_length
+ // compute hashes (data positions)
+ m_context << eth::Instruction::SWAP1;
+ if (_targetType.isDynamicallySized())
+ CompilerUtils(m_context).computeHashStatic();
+ // stack: target_ref source_ref source_length target_length target_data_pos
+ m_context << eth::Instruction::SWAP1;
+ convertLengthToSize(_targetType);
+ m_context << eth::Instruction::DUP2 << eth::Instruction::ADD;
+ // stack: target_ref source_ref source_length target_data_pos target_data_end
+ m_context << eth::Instruction::SWAP3;
+ // stack: target_ref target_data_end source_length target_data_pos source_ref
+
+ eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
+
+ // special case for short byte arrays: Store them together with their length.
+ if (_targetType.isByteArray())
+ {
+ // stack: target_ref target_data_end source_length target_data_pos source_ref
+ m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
+ eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
+ // store the short byte array
+ solAssert(_sourceType.isByteArray(), "");
+ if (_sourceType.location() == DataLocation::Storage)
+ {
+ // just copy the slot, it contains length and data
+ m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
+ m_context << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
+ }
+ else
+ {
+ m_context << eth::Instruction::DUP1;
+ CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false);
+ // stack: target_ref target_data_end source_length target_data_pos source_ref value
+ // clear the lower-order byte - which will hold the length
+ m_context << u256(0xff) << eth::Instruction::NOT << eth::Instruction::AND;
+ // fetch the length and shift it left by one
+ m_context << eth::Instruction::DUP4 << eth::Instruction::DUP1 << eth::Instruction::ADD;
+ // combine value and length and store them
+ m_context << eth::Instruction::OR << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
+ }
+ // end of special case, jump right into cleaning target data area
+ m_context.appendJumpTo(copyLoopEndWithoutByteOffset);
+ m_context << longByteArray;
+ // Store length (2*length+1)
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP1 << eth::Instruction::ADD;
+ m_context << u256(1) << eth::Instruction::ADD;
+ m_context << eth::Instruction::DUP6 << eth::Instruction::SSTORE;
+ }
+
+ // skip copying if source length is zero
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
+
+ if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized())
+ CompilerUtils(m_context).computeHashStatic();
+ // stack: target_ref target_data_end source_length target_data_pos source_data_pos
+ m_context << eth::Instruction::SWAP2;
+ convertLengthToSize(_sourceType);
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
+ if (haveByteOffsetTarget)
+ m_context << u256(0);
+ if (haveByteOffsetSource)
+ m_context << u256(0);
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
+ eth::AssemblyItem copyLoopStart = m_context.newTag();
+ m_context << copyLoopStart;
+ // check for loop condition
+ m_context
+ << eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize)
+ << eth::Instruction::GT << eth::Instruction::ISZERO;
+ eth::AssemblyItem copyLoopEnd = m_context.appendConditionalJump();
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
+ // copy
+ if (sourceBaseType->category() == Type::Category::Array)
+ {
+ solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
+ auto const& sourceBaseArrayType = dynamic_cast<ArrayType const&>(*sourceBaseType);
+ m_context << eth::Instruction::DUP3;
+ if (sourceBaseArrayType.location() == DataLocation::Memory)
+ m_context << eth::Instruction::MLOAD;
+ m_context << eth::Instruction::DUP3;
+ copyArrayToStorage(dynamic_cast<ArrayType const&>(*targetBaseType), sourceBaseArrayType);
+ m_context << eth::Instruction::POP;
+ }
+ else if (directCopy)
+ {
+ solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
+ m_context
+ << eth::Instruction::DUP3 << eth::Instruction::SLOAD
+ << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
+ }
+ else
+ {
+ // Note that we have to copy each element on its own in case conversion is involved.
+ // We might copy too much if there is padding at the last element, but this way end
+ // checking is easier.
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
+ m_context << eth::dupInstruction(3 + byteOffsetSize);
+ if (_sourceType.location() == DataLocation::Storage)
+ {
+ if (haveByteOffsetSource)
+ m_context << eth::Instruction::DUP2;
+ else
+ m_context << u256(0);
+ StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
+ }
+ else if (sourceBaseType->isValueType())
+ CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false);
+ else
+ solAssert(false, "Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
+ solAssert(
+ 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
+ "Stack too deep, try removing local variables."
+ );
+ // fetch target storage reference
+ m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack());
+ if (haveByteOffsetTarget)
+ m_context << eth::dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack());
+ else
+ m_context << u256(0);
+ StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
+ }
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
+ // increment source
+ if (haveByteOffsetSource)
+ incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
+ else
+ {
+ m_context << eth::swapInstruction(2 + byteOffsetSize);
+ if (sourceIsStorage)
+ m_context << sourceBaseType->storageSize();
+ else if (_sourceType.location() == DataLocation::Memory)
+ m_context << sourceBaseType->memoryHeadSize();
+ else
+ m_context << sourceBaseType->calldataEncodedSize(true);
+ m_context
+ << eth::Instruction::ADD
+ << eth::swapInstruction(2 + byteOffsetSize);
+ }
+ // increment target
+ if (haveByteOffsetTarget)
+ incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
+ else
+ m_context
+ << eth::swapInstruction(1 + byteOffsetSize)
+ << targetBaseType->storageSize()
+ << eth::Instruction::ADD
+ << eth::swapInstruction(1 + byteOffsetSize);
+ m_context.appendJumpTo(copyLoopStart);
+ m_context << copyLoopEnd;
+ if (haveByteOffsetTarget)
+ {
+ // clear elements that might be left over in the current slot in target
+ // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
+ m_context << eth::dupInstruction(byteOffsetSize) << eth::Instruction::ISZERO;
+ eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump();
+ m_context << eth::dupInstruction(2 + byteOffsetSize) << eth::dupInstruction(1 + byteOffsetSize);
+ StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true);
+ incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
+ m_context.appendJumpTo(copyLoopEnd);
+
+ m_context << copyCleanupLoopEnd;
+ m_context << eth::Instruction::POP; // might pop the source, but then target is popped next
+ }
+ if (haveByteOffsetSource)
+ m_context << eth::Instruction::POP;
+ m_context << copyLoopEndWithoutByteOffset;
+
+ // zero-out leftovers in target
+ // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
+ m_context << eth::Instruction::POP << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ // stack: target_ref target_data_end target_data_pos_updated
+ clearStorageLoop(*targetBaseType);
+ m_context << eth::Instruction::POP;
+}
+
+void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
+{
+ solAssert(
+ !_sourceType.baseType()->isDynamicallySized(),
+ "Nested dynamic arrays not implemented here."
+ );
+ CompilerUtils utils(m_context);
+ unsigned baseSize = 1;
+ if (!_sourceType.isByteArray())
+ // We always pad the elements, regardless of _padToWordBoundaries.
+ baseSize = _sourceType.baseType()->calldataEncodedSize();
+
+ if (_sourceType.location() == DataLocation::CallData)
+ {
+ if (!_sourceType.isDynamicallySized())
+ m_context << _sourceType.length();
+ if (baseSize > 1)
+ m_context << u256(baseSize) << eth::Instruction::MUL;
+ // stack: target source_offset source_len
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5;
+ // stack: target source_offset source_len source_len source_offset target
+ m_context << eth::Instruction::CALLDATACOPY;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::POP;
+ }
+ else if (_sourceType.location() == DataLocation::Memory)
+ {
+ retrieveLength(_sourceType);
+ // stack: target source length
+ if (!_sourceType.baseType()->isValueType())
+ {
+ // copy using a loop
+ m_context << u256(0) << eth::Instruction::SWAP3;
+ // stack: counter source length target
+ auto repeat = m_context.newTag();
+ m_context << repeat;
+ m_context << eth::Instruction::DUP2 << eth::Instruction::DUP5;
+ m_context << eth::Instruction::LT << eth::Instruction::ISZERO;
+ auto loopEnd = m_context.appendConditionalJump();
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP5;
+ accessIndex(_sourceType, false);
+ MemoryItem(m_context, *_sourceType.baseType(), true).retrieveValue(SourceLocation(), true);
+ if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
+ copyArrayToMemory(*baseArray, _padToWordBoundaries);
+ else
+ utils.storeInMemoryDynamic(*_sourceType.baseType());
+ m_context << eth::Instruction::SWAP3 << u256(1) << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP3;
+ m_context.appendJumpTo(repeat);
+ m_context << loopEnd;
+ m_context << eth::Instruction::SWAP3;
+ utils.popStackSlots(3);
+ // stack: updated_target_pos
+ return;
+ }
+
+ // memcpy using the built-in contract
+ if (_sourceType.isDynamicallySized())
+ {
+ // change pointer to data part
+ m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP1;
+ }
+ // convert length to size
+ if (baseSize > 1)
+ m_context << u256(baseSize) << eth::Instruction::MUL;
+ // stack: <target> <source> <size>
+ //@TODO do not use ::CALL if less than 32 bytes?
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::DUP4;
+ utils.memoryCopy();
+
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ // stack: <target> <size>
+
+ bool paddingNeeded = false;
+ if (_sourceType.isDynamicallySized())
+ paddingNeeded = _padToWordBoundaries && ((baseSize % 32) != 0);
+ else
+ paddingNeeded = _padToWordBoundaries && (((_sourceType.length() * baseSize) % 32) != 0);
+ if (paddingNeeded)
+ {
+ // stack: <target> <size>
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
+ // stack: <length> <target + size>
+ m_context << eth::Instruction::SWAP1 << u256(31) << eth::Instruction::AND;
+ // stack: <target + size> <remainder = size % 32>
+ eth::AssemblyItem skip = m_context.newTag();
+ if (_sourceType.isDynamicallySized())
+ {
+ m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(skip);
+ }
+ // round off, load from there.
+ // stack <target + size> <remainder = size % 32>
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3;
+ m_context << eth::Instruction::SUB;
+ // stack: target+size remainder <target + size - remainder>
+ m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
+ // Now we AND it with ~(2**(8 * (32 - remainder)) - 1)
+ m_context << u256(1);
+ m_context << eth::Instruction::DUP4 << u256(32) << eth::Instruction::SUB;
+ // stack: ...<v> 1 <32 - remainder>
+ m_context << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SUB;
+ m_context << eth::Instruction::NOT << eth::Instruction::AND;
+ // stack: target+size remainder target+size-remainder <v & ...>
+ m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
+ // stack: target+size remainder target+size-remainder
+ m_context << u256(32) << eth::Instruction::ADD;
+ // stack: target+size remainder <new_padded_end>
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::POP;
+
+ if (_sourceType.isDynamicallySized())
+ m_context << skip.tag();
+ // stack <target + "size"> <remainder = size % 32>
+ m_context << eth::Instruction::POP;
+ }
+ else
+ // stack: <target> <size>
+ m_context << eth::Instruction::ADD;
+ }
+ else
+ {
+ solAssert(_sourceType.location() == DataLocation::Storage, "");
+ unsigned storageBytes = _sourceType.baseType()->storageBytes();
+ u256 storageSize = _sourceType.baseType()->storageSize();
+ solAssert(storageSize > 1 || (storageSize == 1 && storageBytes > 0), "");
+
+ retrieveLength(_sourceType);
+ // stack here: memory_offset storage_offset length
+ // jump to end if length is zero
+ m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
+ eth::AssemblyItem loopEnd = m_context.appendConditionalJump();
+ // Special case for tightly-stored byte arrays
+ if (_sourceType.isByteArray())
+ {
+ // stack here: memory_offset storage_offset length
+ m_context << eth::Instruction::DUP1 << u256(31) << eth::Instruction::LT;
+ eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
+ // store the short byte array (discard lower-order byte)
+ m_context << u256(0x100) << eth::Instruction::DUP1;
+ m_context << eth::Instruction::DUP4 << eth::Instruction::SLOAD;
+ m_context << eth::Instruction::DIV << eth::Instruction::MUL;
+ m_context << eth::Instruction::DUP4 << eth::Instruction::MSTORE;
+ // stack here: memory_offset storage_offset length
+ // add 32 or length to memory offset
+ m_context << eth::Instruction::SWAP2;
+ if (_padToWordBoundaries)
+ m_context << u256(32);
+ else
+ m_context << eth::Instruction::DUP3;
+ m_context << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2;
+ m_context.appendJumpTo(loopEnd);
+ m_context << longByteArray;
+ }
+ // compute memory end offset
+ if (baseSize > 1)
+ // convert length to memory size
+ m_context << u256(baseSize) << eth::Instruction::MUL;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
+ if (_sourceType.isDynamicallySized())
+ {
+ // actual array data is stored at SHA3(storage_offset)
+ m_context << eth::Instruction::SWAP1;
+ utils.computeHashStatic();
+ m_context << eth::Instruction::SWAP1;
+ }
+
+ // stack here: memory_end_offset storage_data_offset memory_offset
+ bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16;
+ if (haveByteOffset)
+ m_context << u256(0) << eth::Instruction::SWAP1;
+ // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
+ eth::AssemblyItem loopStart = m_context.newTag();
+ m_context << loopStart;
+ // load and store
+ if (_sourceType.isByteArray())
+ {
+ // Packed both in storage and memory.
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
+ // increment storage_data_offset by 1
+ m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
+ // increment memory offset by 32
+ m_context << eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD;
+ }
+ else
+ {
+ // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
+ if (haveByteOffset)
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
+ else
+ m_context << eth::Instruction::DUP2 << u256(0);
+ StorageItem(m_context, *_sourceType.baseType()).retrieveValue(SourceLocation(), true);
+ if (auto baseArray = dynamic_cast<ArrayType const*>(_sourceType.baseType().get()))
+ copyArrayToMemory(*baseArray, _padToWordBoundaries);
+ else
+ utils.storeInMemoryDynamic(*_sourceType.baseType());
+ // increment storage_data_offset and byte offset
+ if (haveByteOffset)
+ incrementByteOffset(storageBytes, 2, 3);
+ else
+ {
+ m_context << eth::Instruction::SWAP1;
+ m_context << storageSize << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP1;
+ }
+ }
+ // check for loop condition
+ m_context << eth::Instruction::DUP1 << eth::dupInstruction(haveByteOffset ? 5 : 4);
+ m_context << eth::Instruction::GT;
+ m_context.appendConditionalJumpTo(loopStart);
+ // stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
+ if (haveByteOffset)
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ if (_padToWordBoundaries && baseSize % 32 != 0)
+ {
+ // memory_end_offset - start is the actual length (we want to compute the ceil of).
+ // memory_offset - start is its next multiple of 32, but it might be off by 32.
+ // so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31
+ m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ m_context << u256(31) << eth::Instruction::AND;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2;
+ }
+ m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
+ }
+}
+
+void ArrayUtils::clearArray(ArrayType const& _type) const
+{
+ unsigned stackHeightStart = m_context.stackHeight();
+ solAssert(_type.location() == DataLocation::Storage, "");
+ if (_type.baseType()->storageBytes() < 32)
+ {
+ solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
+ solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type.");
+ }
+ if (_type.baseType()->isValueType())
+ solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
+
+ m_context << eth::Instruction::POP; // remove byte offset
+ if (_type.isDynamicallySized())
+ clearDynamicArray(_type);
+ else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
+ m_context << eth::Instruction::POP;
+ else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
+ {
+ // unroll loop for small arrays @todo choose a good value
+ // Note that we loop over storage slots here, not elements.
+ for (unsigned i = 1; i < _type.storageSize(); ++i)
+ m_context
+ << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
+ << u256(1) << eth::Instruction::ADD;
+ m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ }
+ else if (!_type.baseType()->isValueType() && _type.length() <= 4)
+ {
+ // unroll loop for small arrays @todo choose a good value
+ solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size.");
+ for (unsigned i = 1; i < _type.length(); ++i)
+ {
+ m_context << u256(0);
+ StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false);
+ m_context
+ << eth::Instruction::POP
+ << u256(_type.baseType()->storageSize()) << eth::Instruction::ADD;
+ }
+ m_context << u256(0);
+ StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true);
+ }
+ else
+ {
+ m_context << eth::Instruction::DUP1 << _type.length();
+ convertLengthToSize(_type);
+ m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
+ if (_type.baseType()->storageBytes() < 32)
+ clearStorageLoop(IntegerType(256));
+ else
+ clearStorageLoop(*_type.baseType());
+ m_context << eth::Instruction::POP;
+ }
+ solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
+}
+
+void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
+{
+ solAssert(_type.location() == DataLocation::Storage, "");
+ solAssert(_type.isDynamicallySized(), "");
+
+ // fetch length
+ retrieveLength(_type);
+ // set length to zero
+ m_context << u256(0) << eth::Instruction::DUP3 << eth::Instruction::SSTORE;
+ // Special case: short byte arrays are stored togeher with their length
+ eth::AssemblyItem endTag = m_context.newTag();
+ if (_type.isByteArray())
+ {
+ // stack: ref old_length
+ m_context << eth::Instruction::DUP1 << u256(31) << eth::Instruction::LT;
+ eth::AssemblyItem longByteArray = m_context.appendConditionalJump();
+ m_context << eth::Instruction::POP;
+ m_context.appendJumpTo(endTag);
+ m_context.adjustStackOffset(1); // needed because of jump
+ m_context << longByteArray;
+ }
+ // stack: ref old_length
+ convertLengthToSize(_type);
+ // compute data positions
+ m_context << eth::Instruction::SWAP1;
+ CompilerUtils(m_context).computeHashStatic();
+ // stack: len data_pos
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
+ << eth::Instruction::SWAP1;
+ // stack: data_pos_end data_pos
+ if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
+ clearStorageLoop(IntegerType(256));
+ else
+ clearStorageLoop(*_type.baseType());
+ // cleanup
+ m_context << endTag;
+ m_context << eth::Instruction::POP;
+}
+
+void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
+{
+ solAssert(_type.location() == DataLocation::Storage, "");
+ solAssert(_type.isDynamicallySized(), "");
+ if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
+ solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
+
+ unsigned stackHeightStart = m_context.stackHeight();
+ eth::AssemblyItem resizeEnd = m_context.newTag();
+
+ // stack: ref new_length
+ // fetch old length
+ retrieveLength(_type, 1);
+ // stack: ref new_length old_length
+ solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "2");
+
+ // Special case for short byte arrays, they are stored together with their length
+ if (_type.isByteArray())
+ {
+ eth::AssemblyItem regularPath = m_context.newTag();
+ // We start by a large case-distinction about the old and new length of the byte array.
+
+ m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
+ // stack: ref new_length current_length ref_value
+
+ solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
+ m_context << eth::Instruction::DUP2 << u256(31) << eth::Instruction::LT;
+ eth::AssemblyItem currentIsLong = m_context.appendConditionalJump();
+ m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
+ eth::AssemblyItem newIsLong = m_context.appendConditionalJump();
+
+ // Here: short -> short
+
+ // Compute 1 << (256 - 8 * new_size)
+ eth::AssemblyItem shortToShort = m_context.newTag();
+ m_context << shortToShort;
+ m_context << eth::Instruction::DUP3 << u256(8) << eth::Instruction::MUL;
+ m_context << u256(0x100) << eth::Instruction::SUB;
+ m_context << u256(2) << eth::Instruction::EXP;
+ // Divide and multiply by that value, clearing bits.
+ m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
+ m_context << eth::Instruction::DIV << eth::Instruction::MUL;
+ // Insert 2*length.
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP1 << eth::Instruction::ADD;
+ m_context << eth::Instruction::OR;
+ // Store.
+ m_context << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
+ solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
+ m_context.appendJumpTo(resizeEnd);
+
+ m_context.adjustStackOffset(1); // we have to do that because of the jumps
+ // Here: short -> long
+
+ m_context << newIsLong;
+ // stack: ref new_length current_length ref_value
+ solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
+ // Zero out lower-order byte.
+ m_context << u256(0xff) << eth::Instruction::NOT << eth::Instruction::AND;
+ // Store at data location.
+ m_context << eth::Instruction::DUP4;
+ CompilerUtils(m_context).computeHashStatic();
+ m_context << eth::Instruction::SSTORE;
+ // stack: ref new_length current_length
+ // Store new length: Compule 2*length + 1 and store it.
+ m_context << eth::Instruction::DUP2 << eth::Instruction::DUP1 << eth::Instruction::ADD;
+ m_context << u256(1) << eth::Instruction::ADD;
+ // stack: ref new_length current_length 2*new_length+1
+ m_context << eth::Instruction::DUP4 << eth::Instruction::SSTORE;
+ solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
+ m_context.appendJumpTo(resizeEnd);
+
+ m_context.adjustStackOffset(1); // we have to do that because of the jumps
+
+ m_context << currentIsLong;
+ m_context << eth::Instruction::DUP3 << u256(31) << eth::Instruction::LT;
+ m_context.appendConditionalJumpTo(regularPath);
+
+ // Here: long -> short
+ // Read the first word of the data and store it on the stack. Clear the data location and
+ // then jump to the short -> short case.
+
+ // stack: ref new_length current_length ref_value
+ solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
+ m_context << eth::Instruction::POP << eth::Instruction::DUP3;
+ CompilerUtils(m_context).computeHashStatic();
+ m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
+ // stack: ref new_length current_length first_word data_location
+ m_context << eth::Instruction::DUP3;
+ convertLengthToSize(_type);
+ m_context << eth::Instruction::DUP2 << eth::Instruction::ADD << eth::Instruction::SWAP1;
+ // stack: ref new_length current_length first_word data_location_end data_location
+ clearStorageLoop(IntegerType(256));
+ m_context << eth::Instruction::POP;
+ // stack: ref new_length current_length first_word
+ solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
+ m_context.appendJumpTo(shortToShort);
+
+ m_context << regularPath;
+ // stack: ref new_length current_length ref_value
+ m_context << eth::Instruction::POP;
+ }
+
+ // Change of length for a regular array (i.e. length at location, data at sha3(location)).
+ // stack: ref new_length old_length
+ // store new length
+ m_context << eth::Instruction::DUP2;
+ if (_type.isByteArray())
+ // For a "long" byte array, store length as 2*length+1
+ m_context << eth::Instruction::DUP1 << eth::Instruction::ADD << u256(1) << eth::Instruction::ADD;
+ m_context<< eth::Instruction::DUP4 << eth::Instruction::SSTORE;
+ // skip if size is not reduced
+ m_context << eth::Instruction::DUP2 << eth::Instruction::DUP2
+ << eth::Instruction::ISZERO << eth::Instruction::GT;
+ m_context.appendConditionalJumpTo(resizeEnd);
+
+ // size reduced, clear the end of the array
+ // stack: ref new_length old_length
+ convertLengthToSize(_type);
+ m_context << eth::Instruction::DUP2;
+ convertLengthToSize(_type);
+ // stack: ref new_length old_size new_size
+ // compute data positions
+ m_context << eth::Instruction::DUP4;
+ CompilerUtils(m_context).computeHashStatic();
+ // stack: ref new_length old_size new_size data_pos
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ // stack: ref new_length data_pos new_size delete_end
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
+ // stack: ref new_length delete_end delete_start
+ if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
+ clearStorageLoop(IntegerType(256));
+ else
+ clearStorageLoop(*_type.baseType());
+
+ m_context << resizeEnd;
+ // cleanup
+ m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
+ solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
+}
+
+void ArrayUtils::clearStorageLoop(Type const& _type) const
+{
+ unsigned stackHeightStart = m_context.stackHeight();
+ if (_type.category() == Type::Category::Mapping)
+ {
+ m_context << eth::Instruction::POP;
+ return;
+ }
+ // stack: end_pos pos
+
+ // jump to and return from the loop to allow for duplicate code removal
+ eth::AssemblyItem returnTag = m_context.pushNewTag();
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
+
+ // stack: <return tag> end_pos pos
+ eth::AssemblyItem loopStart = m_context.appendJumpToNew();
+ m_context << loopStart;
+ // check for loop condition
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3
+ << eth::Instruction::GT << eth::Instruction::ISZERO;
+ eth::AssemblyItem zeroLoopEnd = m_context.newTag();
+ m_context.appendConditionalJumpTo(zeroLoopEnd);
+ // delete
+ m_context << u256(0);
+ StorageItem(m_context, _type).setToZero(SourceLocation(), false);
+ m_context << eth::Instruction::POP;
+ // increment
+ m_context << u256(1) << eth::Instruction::ADD;
+ m_context.appendJumpTo(loopStart);
+ // cleanup
+ m_context << zeroLoopEnd;
+ m_context << eth::Instruction::POP << eth::Instruction::SWAP1;
+ // "return"
+ m_context << eth::Instruction::JUMP;
+
+ m_context << returnTag;
+ solAssert(m_context.stackHeight() == stackHeightStart - 1, "");
+}
+
+void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
+{
+ if (_arrayType.location() == DataLocation::Storage)
+ {
+ if (_arrayType.baseType()->storageSize() <= 1)
+ {
+ unsigned baseBytes = _arrayType.baseType()->storageBytes();
+ if (baseBytes == 0)
+ m_context << eth::Instruction::POP << u256(1);
+ else if (baseBytes <= 16)
+ {
+ unsigned itemsPerSlot = 32 / baseBytes;
+ m_context
+ << u256(itemsPerSlot - 1) << eth::Instruction::ADD
+ << u256(itemsPerSlot) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ }
+ }
+ else
+ m_context << _arrayType.baseType()->storageSize() << eth::Instruction::MUL;
+ }
+ else
+ {
+ if (!_arrayType.isByteArray())
+ {
+ if (_arrayType.location() == DataLocation::Memory)
+ m_context << _arrayType.baseType()->memoryHeadSize();
+ else
+ m_context << _arrayType.baseType()->calldataEncodedSize();
+ m_context << eth::Instruction::MUL;
+ }
+ else if (_pad)
+ m_context << u256(31) << eth::Instruction::ADD
+ << u256(32) << eth::Instruction::DUP1
+ << eth::Instruction::SWAP2 << eth::Instruction::DIV << eth::Instruction::MUL;
+ }
+}
+
+void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDepth) const
+{
+ if (!_arrayType.isDynamicallySized())
+ m_context << _arrayType.length();
+ else
+ {
+ m_context << eth::dupInstruction(1 + _stackDepth);
+ switch (_arrayType.location())
+ {
+ case DataLocation::CallData:
+ // length is stored on the stack
+ break;
+ case DataLocation::Memory:
+ m_context << eth::Instruction::MLOAD;
+ break;
+ case DataLocation::Storage:
+ m_context << eth::Instruction::SLOAD;
+ if (_arrayType.isByteArray())
+ {
+ // Retrieve length both for in-place strings and off-place strings:
+ // Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
+ // i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
+ // computes (x & (-1)) / 2, which is equivalent to just x / 2.
+ m_context << u256(1) << eth::Instruction::DUP2 << u256(1) << eth::Instruction::AND;
+ m_context << eth::Instruction::ISZERO << u256(0x100) << eth::Instruction::MUL;
+ m_context << eth::Instruction::SUB << eth::Instruction::AND;
+ m_context << u256(2) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ }
+ break;
+ }
+ }
+}
+
+void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) const
+{
+ /// Stack: reference [length] index
+ DataLocation location = _arrayType.location();
+
+ if (_doBoundsCheck)
+ {
+ // retrieve length
+ ArrayUtils::retrieveLength(_arrayType, 1);
+ // Stack: ref [length] index length
+ // check out-of-bounds access
+ m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
+ // out-of-bounds access throws exception
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+ if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
+ // remove length if present
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+
+ // stack: <base_ref> <index>
+ m_context << eth::Instruction::SWAP1;
+ // stack: <index> <base_ref>
+ switch (location)
+ {
+ case DataLocation::Memory:
+ if (_arrayType.isDynamicallySized())
+ m_context << u256(32) << eth::Instruction::ADD;
+ // fall-through
+ case DataLocation::CallData:
+ if (!_arrayType.isByteArray())
+ {
+ m_context << eth::Instruction::SWAP1;
+ if (location == DataLocation::CallData)
+ m_context << _arrayType.baseType()->calldataEncodedSize();
+ else
+ m_context << u256(_arrayType.memoryHeadSize());
+ m_context << eth::Instruction::MUL;
+ }
+ m_context << eth::Instruction::ADD;
+ break;
+ case DataLocation::Storage:
+ {
+ eth::AssemblyItem endTag = m_context.newTag();
+ if (_arrayType.isByteArray())
+ {
+ // Special case of short byte arrays.
+ m_context << eth::Instruction::SWAP1;
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ m_context << u256(1) << eth::Instruction::AND << eth::Instruction::ISZERO;
+ // No action needed for short byte arrays.
+ m_context.appendConditionalJumpTo(endTag);
+ m_context << eth::Instruction::SWAP1;
+ }
+ if (_arrayType.isDynamicallySized())
+ CompilerUtils(m_context).computeHashStatic();
+ m_context << eth::Instruction::SWAP1;
+ if (_arrayType.baseType()->storageBytes() <= 16)
+ {
+ // stack: <data_ref> <index>
+ // goal:
+ // <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
+ unsigned byteSize = _arrayType.baseType()->storageBytes();
+ solAssert(byteSize != 0, "");
+ unsigned itemsPerSlot = 32 / byteSize;
+ m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
+ // stack: itemsPerSlot index data_ref
+ m_context
+ << eth::Instruction::DUP3 << eth::Instruction::DUP3
+ << eth::Instruction::DIV << eth::Instruction::ADD
+ // stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
+ << eth::Instruction::SWAP2 << eth::Instruction::SWAP1
+ << eth::Instruction::MOD;
+ if (byteSize != 1)
+ m_context << u256(byteSize) << eth::Instruction::MUL;
+ }
+ else
+ {
+ if (_arrayType.baseType()->storageSize() != 1)
+ m_context << _arrayType.baseType()->storageSize() << eth::Instruction::MUL;
+ m_context << eth::Instruction::ADD << u256(0);
+ }
+ m_context << endTag;
+ break;
+ }
+ default:
+ solAssert(false, "");
+ }
+}
+
+void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
+{
+ solAssert(_byteSize < 32, "");
+ solAssert(_byteSize != 0, "");
+ // We do the following, but avoiding jumps:
+ // byteOffset += byteSize
+ // if (byteOffset + byteSize > 32)
+ // {
+ // storageOffset++;
+ // byteOffset = 0;
+ // }
+ if (_byteOffsetPosition > 1)
+ m_context << eth::swapInstruction(_byteOffsetPosition - 1);
+ m_context << u256(_byteSize) << eth::Instruction::ADD;
+ if (_byteOffsetPosition > 1)
+ m_context << eth::swapInstruction(_byteOffsetPosition - 1);
+ // compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
+ m_context
+ << u256(32) << eth::dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
+ << eth::Instruction::ADD << eth::Instruction::DIV;
+ // increment storage offset if X == 1 (just add X to it)
+ // stack: X
+ m_context
+ << eth::swapInstruction(_storageOffsetPosition) << eth::dupInstruction(_storageOffsetPosition + 1)
+ << eth::Instruction::ADD << eth::swapInstruction(_storageOffsetPosition);
+ // stack: X
+ // set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
+ m_context << u256(1) << eth::Instruction::SUB;
+ // stack: 1 - X
+ if (_byteOffsetPosition == 1)
+ m_context << eth::Instruction::MUL;
+ else
+ m_context
+ << eth::dupInstruction(_byteOffsetPosition + 1) << eth::Instruction::MUL
+ << eth::swapInstruction(_byteOffsetPosition) << eth::Instruction::POP;
+}
diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h
new file mode 100644
index 00000000..53d36c14
--- /dev/null
+++ b/libsolidity/codegen/ArrayUtils.h
@@ -0,0 +1,103 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Code generation utils that handle arrays.
+ */
+
+#pragma once
+
+namespace dev
+{
+namespace solidity
+{
+
+class CompilerContext;
+class Type;
+class ArrayType;
+
+/**
+ * Class that provides code generation for handling arrays.
+ */
+class ArrayUtils
+{
+public:
+ ArrayUtils(CompilerContext& _context): m_context(_context) {}
+
+ /// Copies an array to an array in storage. The arrays can be of different types only if
+ /// their storage representation is the same.
+ /// Stack pre: source_reference [source_length] target_reference
+ /// Stack post: target_reference
+ void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
+ /// Copies the data part of an array (which cannot be dynamically nested) from anywhere
+ /// to a given position in memory.
+ /// This always copies contained data as is (i.e. structs and fixed-size arrays are copied in
+ /// place as required by the ABI encoding). Use CompilerUtils::convertType if you want real
+ /// memory copies of nested arrays.
+ /// Stack pre: memory_offset source_item
+ /// Stack post: memory_offest + length(padded)
+ void copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries = true) const;
+ /// Clears the given dynamic or static array.
+ /// Stack pre: storage_ref storage_byte_offset
+ /// Stack post:
+ void clearArray(ArrayType const& _type) const;
+ /// Clears the length and data elements of the array referenced on the stack.
+ /// Stack pre: reference (excludes byte offset)
+ /// Stack post:
+ void clearDynamicArray(ArrayType const& _type) const;
+ /// Changes the size of a dynamic array and clears the tail if it is shortened.
+ /// Stack pre: reference (excludes byte offset) new_length
+ /// Stack post:
+ void resizeDynamicArray(ArrayType const& _type) const;
+ /// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
+ /// Stack pre: end_ref start_ref
+ /// Stack post: end_ref
+ void clearStorageLoop(Type const& _type) const;
+ /// Converts length to size (number of storage slots or calldata/memory bytes).
+ /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory.
+ /// Stack pre: length
+ /// Stack post: size
+ void convertLengthToSize(ArrayType const& _arrayType, bool _pad = false) const;
+ /// Retrieves the length (number of elements) of the array ref on the stack. This also
+ /// works for statically-sized arrays.
+ /// @param _stackDepth number of stack elements between top of stack and top (!) of reference
+ /// Stack pre: reference (excludes byte offset for dynamic storage arrays)
+ /// Stack post: reference length
+ void retrieveLength(ArrayType const& _arrayType, unsigned _stackDepth = 0) const;
+ /// Stores the length of an array of type @a _arrayType in storage. The length itself is stored
+ /// on the stack at position @a _stackDepthLength and the storage reference at @a _stackDepthRef.
+ /// If @a _arrayType is a byte array, takes tight coding into account.
+ void storeLength(ArrayType const& _arrayType, unsigned _stackDepthLength = 0, unsigned _stackDepthRef = 1) const;
+ /// Performs bounds checking and returns a reference on the stack.
+ /// Stack pre: reference [length] index
+ /// Stack post (storage): storage_slot byte_offset
+ /// Stack post: memory/calldata_offset
+ void accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck = true) const;
+
+private:
+ /// Adds the given number of bytes to a storage byte offset counter and also increments
+ /// the storage offset if adding this number again would increase the counter over 32.
+ /// @param byteOffsetPosition the stack offset of the storage byte offset
+ /// @param storageOffsetPosition the stack offset of the storage slot offset
+ void incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const;
+
+ CompilerContext& m_context;
+};
+
+}
+}
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
new file mode 100644
index 00000000..c7eb71a8
--- /dev/null
+++ b/libsolidity/codegen/Compiler.cpp
@@ -0,0 +1,788 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity compiler.
+ */
+
+#include <libsolidity/codegen/Compiler.h>
+#include <algorithm>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libevmcore/Instruction.h>
+#include <libethcore/ChainOperationParams.h>
+#include <libevmasm/Assembly.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/ExpressionCompiler.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+/**
+ * Simple helper class to ensure that the stack height is the same at certain places in the code.
+ */
+class StackHeightChecker
+{
+public:
+ StackHeightChecker(CompilerContext const& _context):
+ m_context(_context), stackHeight(m_context.stackHeight()) {}
+ void check() { solAssert(m_context.stackHeight() == stackHeight, "I sense a disturbance in the stack."); }
+private:
+ CompilerContext const& m_context;
+ unsigned stackHeight;
+};
+
+void Compiler::compileContract(
+ ContractDefinition const& _contract,
+ std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
+)
+{
+ m_context = CompilerContext();
+ {
+ CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
+ initializeContext(_contract, _contracts);
+ appendFunctionSelector(_contract);
+ appendFunctionsWithoutCode();
+ }
+
+ // Swap the runtime context with the creation-time context
+ swap(m_context, m_runtimeContext);
+ CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
+ initializeContext(_contract, _contracts);
+ packIntoContractCreator(_contract, m_runtimeContext);
+ if (m_optimize)
+ m_context.optimise(m_optimizeRuns);
+
+ if (_contract.isLibrary())
+ {
+ solAssert(m_runtimeSub != size_t(-1), "");
+ m_context.injectVersionStampIntoSub(m_runtimeSub);
+ }
+}
+
+void Compiler::compileClone(
+ ContractDefinition const& _contract,
+ map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+)
+{
+ m_context = CompilerContext(); // clear it just in case
+ initializeContext(_contract, _contracts);
+
+ appendInitAndConstructorCode(_contract);
+
+ //@todo determine largest return size of all runtime functions
+ eth::AssemblyItem runtimeSub = m_context.addSubroutine(cloneRuntime());
+ solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
+ m_runtimeSub = size_t(runtimeSub.data());
+
+ // stack contains sub size
+ m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
+ m_context << u256(0) << eth::Instruction::RETURN;
+
+ appendFunctionsWithoutCode();
+
+ if (m_optimize)
+ m_context.optimise(m_optimizeRuns);
+}
+
+eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
+{
+ return m_runtimeContext.functionEntryLabelIfExists(_function);
+}
+
+void Compiler::initializeContext(
+ ContractDefinition const& _contract,
+ map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
+)
+{
+ m_context.setCompiledContracts(_compiledContracts);
+ m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
+ CompilerUtils(m_context).initialiseFreeMemoryPointer();
+ registerStateVariables(_contract);
+ m_context.resetVisitedNodes(&_contract);
+}
+
+void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
+{
+ // Determine the arguments that are used for the base constructors.
+ std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
+ for (ContractDefinition const* contract: bases)
+ {
+ if (FunctionDefinition const* constructor = contract->constructor())
+ for (auto const& modifier: constructor->modifiers())
+ {
+ auto baseContract = dynamic_cast<ContractDefinition const*>(
+ modifier->name()->annotation().referencedDeclaration);
+ if (baseContract)
+ if (m_baseArguments.count(baseContract->constructor()) == 0)
+ m_baseArguments[baseContract->constructor()] = &modifier->arguments();
+ }
+
+ for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
+ {
+ ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
+ base->name().annotation().referencedDeclaration
+ );
+ solAssert(baseContract, "");
+
+ if (m_baseArguments.count(baseContract->constructor()) == 0)
+ m_baseArguments[baseContract->constructor()] = &base->arguments();
+ }
+ }
+ // Initialization of state variables in base-to-derived order.
+ for (ContractDefinition const* contract: boost::adaptors::reverse(bases))
+ initializeStateVariables(*contract);
+
+ if (FunctionDefinition const* constructor = _contract.constructor())
+ appendConstructor(*constructor);
+ else if (auto c = m_context.nextConstructor(_contract))
+ appendBaseConstructor(*c);
+}
+
+void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
+{
+ appendInitAndConstructorCode(_contract);
+
+ eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly());
+ solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
+ m_runtimeSub = size_t(runtimeSub.data());
+
+ // stack contains sub size
+ m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
+ m_context << u256(0) << eth::Instruction::RETURN;
+
+ // note that we have to include the functions again because of absolute jump labels
+ appendFunctionsWithoutCode();
+}
+
+void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _constructor);
+ FunctionType constructorType(_constructor);
+ if (!constructorType.parameterTypes().empty())
+ {
+ solAssert(m_baseArguments.count(&_constructor), "");
+ std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
+ solAssert(arguments, "");
+ for (unsigned i = 0; i < arguments->size(); ++i)
+ compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]);
+ }
+ _constructor.accept(*this);
+}
+
+void Compiler::appendConstructor(FunctionDefinition const& _constructor)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _constructor);
+ // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
+ if (!_constructor.parameters().empty())
+ {
+ unsigned argumentSize = 0;
+ for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters())
+ if (var->annotation().type->isDynamicallySized())
+ {
+ argumentSize = 0;
+ break;
+ }
+ else
+ argumentSize += var->annotation().type->calldataEncodedSize();
+
+ CompilerUtils(m_context).fetchFreeMemoryPointer();
+ if (argumentSize == 0)
+ {
+ // argument size is dynamic, use CODESIZE to determine it
+ m_context.appendProgramSize(); // program itself
+ // CODESIZE is program plus manually added arguments
+ m_context << eth::Instruction::CODESIZE << eth::Instruction::SUB;
+ }
+ else
+ m_context << u256(argumentSize);
+ // stack: <memptr> <argument size>
+ m_context << eth::Instruction::DUP1;
+ m_context.appendProgramSize();
+ m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
+ m_context << eth::Instruction::DUP2 << eth::Instruction::ADD;
+ CompilerUtils(m_context).storeFreeMemoryPointer();
+ // stack: <memptr>
+ appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true);
+ }
+ _constructor.accept(*this);
+}
+
+void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
+{
+ map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
+ map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
+
+ FunctionDefinition const* fallback = _contract.fallbackFunction();
+ eth::AssemblyItem notFound = m_context.newTag();
+ // shortcut messages without data if we have many functions in order to be able to receive
+ // ether with constant gas
+ if (interfaceFunctions.size() > 5 || fallback)
+ {
+ m_context << eth::Instruction::CALLDATASIZE << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(notFound);
+ }
+
+ // retrieve the function signature hash from the calldata
+ if (!interfaceFunctions.empty())
+ CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
+
+ // stack now is: 1 0 <funhash>
+ for (auto const& it: interfaceFunctions)
+ {
+ callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
+ m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
+ m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
+ }
+ m_context.appendJumpTo(notFound);
+
+ m_context << notFound;
+ if (fallback)
+ {
+ eth::AssemblyItem returnTag = m_context.pushNewTag();
+ fallback->accept(*this);
+ m_context << returnTag;
+ appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
+ }
+ else if (_contract.isLibrary())
+ // Reject invalid library calls and ether sent to a library.
+ m_context.appendJumpTo(m_context.errorTag());
+ else
+ m_context << eth::Instruction::STOP; // function not found
+
+ for (auto const& it: interfaceFunctions)
+ {
+ FunctionTypePointer const& functionType = it.second;
+ solAssert(functionType->hasDeclaration(), "");
+ CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
+ m_context << callDataUnpackerEntryPoints.at(it.first);
+ eth::AssemblyItem returnTag = m_context.pushNewTag();
+ m_context << CompilerUtils::dataStartOffset;
+ appendCalldataUnpacker(functionType->parameterTypes());
+ m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
+ m_context << returnTag;
+ appendReturnValuePacker(functionType->returnParameterTypes(), _contract.isLibrary());
+ }
+}
+
+void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
+{
+ // We do not check the calldata size, everything is zero-padded
+
+ //@todo this does not yet support nested dynamic arrays
+
+ // Retain the offset pointer as base_offset, the point from which the data offsets are computed.
+ m_context << eth::Instruction::DUP1;
+ for (TypePointer const& parameterType: _typeParameters)
+ {
+ // stack: v1 v2 ... v(k-1) base_offset current_offset
+ TypePointer type = parameterType->decodingType();
+ if (type->category() == Type::Category::Array)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
+ solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
+ if (_fromMemory)
+ {
+ solAssert(
+ arrayType.baseType()->isValueType(),
+ "Nested memory arrays not yet implemented here."
+ );
+ // @todo If base type is an array or struct, it is still calldata-style encoded, so
+ // we would have to convert it like below.
+ solAssert(arrayType.location() == DataLocation::Memory, "");
+ if (arrayType.isDynamicallySized())
+ {
+ // compute data pointer
+ m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
+ m_context << u256(0x20) << eth::Instruction::ADD;
+ }
+ else
+ {
+ m_context << eth::Instruction::DUP1;
+ m_context << u256(arrayType.calldataEncodedSize(true)) << eth::Instruction::ADD;
+ }
+ }
+ else
+ {
+ // first load from calldata and potentially convert to memory if arrayType is memory
+ TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false);
+ if (calldataType->isDynamicallySized())
+ {
+ // put on stack: data_pointer length
+ CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
+ // stack: base_offset data_offset next_pointer
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ // stack: base_offset next_pointer data_pointer
+ // retrieve length
+ CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
+ // stack: base_offset next_pointer length data_pointer
+ m_context << eth::Instruction::SWAP2;
+ // stack: base_offset data_pointer length next_pointer
+ }
+ else
+ {
+ // leave the pointer on the stack
+ m_context << eth::Instruction::DUP1;
+ m_context << u256(calldataType->calldataEncodedSize()) << eth::Instruction::ADD;
+ }
+ if (arrayType.location() == DataLocation::Memory)
+ {
+ // stack: base_offset calldata_ref [length] next_calldata
+ // copy to memory
+ // move calldata type up again
+ CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
+ CompilerUtils(m_context).convertType(*calldataType, arrayType);
+ // fetch next pointer again
+ CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
+ }
+ // move base_offset up
+ CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
+ m_context << eth::Instruction::SWAP1;
+ }
+ }
+ else
+ {
+ solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
+ CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
+ CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
+ m_context << eth::Instruction::SWAP1;
+ }
+ // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset
+ }
+ m_context << eth::Instruction::POP << eth::Instruction::POP;
+}
+
+void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary)
+{
+ CompilerUtils utils(m_context);
+ if (_typeParameters.empty())
+ m_context << eth::Instruction::STOP;
+ else
+ {
+ utils.fetchFreeMemoryPointer();
+ //@todo optimization: if we return a single memory array, there should be enough space before
+ // its data to add the needed parts and we avoid a memory copy.
+ utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary);
+ utils.toSizeAfterFreeMemoryPointer();
+ m_context << eth::Instruction::RETURN;
+ }
+}
+
+void Compiler::registerStateVariables(ContractDefinition const& _contract)
+{
+ for (auto const& var: ContractType(_contract).stateVariables())
+ m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
+}
+
+void Compiler::initializeStateVariables(ContractDefinition const& _contract)
+{
+ for (VariableDeclaration const* variable: _contract.stateVariables())
+ if (variable->value() && !variable->isConstant())
+ ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
+}
+
+bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
+{
+ solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
+ CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration);
+
+ m_context.startFunction(_variableDeclaration);
+ m_breakTags.clear();
+ m_continueTags.clear();
+
+ if (_variableDeclaration.isConstant())
+ ExpressionCompiler(m_context, m_optimize).appendConstStateVariableAccessor(_variableDeclaration);
+ else
+ ExpressionCompiler(m_context, m_optimize).appendStateVariableAccessor(_variableDeclaration);
+
+ return false;
+}
+
+bool Compiler::visit(FunctionDefinition const& _function)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _function);
+
+ m_context.startFunction(_function);
+
+ // stack upon entry: [return address] [arg0] [arg1] ... [argn]
+ // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
+
+ unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
+ if (!_function.isConstructor())
+ // adding 1 for return address.
+ m_context.adjustStackOffset(parametersSize + 1);
+ for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
+ {
+ m_context.addVariable(*variable, parametersSize);
+ parametersSize -= variable->annotation().type->sizeOnStack();
+ }
+
+ for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
+ appendStackVariableInitialisation(*variable);
+ for (VariableDeclaration const* localVariable: _function.localVariables())
+ appendStackVariableInitialisation(*localVariable);
+
+ if (_function.isConstructor())
+ if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope())))
+ appendBaseConstructor(*c);
+
+ m_returnTag = m_context.newTag();
+ m_breakTags.clear();
+ m_continueTags.clear();
+ m_stackCleanupForReturn = 0;
+ m_currentFunction = &_function;
+ m_modifierDepth = 0;
+
+ appendModifierOrFunctionCode();
+
+ m_context << m_returnTag;
+
+ // Now we need to re-shuffle the stack. For this we keep a record of the stack layout
+ // that shows the target positions of the elements, where "-1" denotes that this element needs
+ // to be removed from the stack.
+ // Note that the fact that the return arguments are of increasing index is vital for this
+ // algorithm to work.
+
+ unsigned const c_argumentsSize = CompilerUtils::sizeOnStack(_function.parameters());
+ unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters());
+ unsigned const c_localVariablesSize = CompilerUtils::sizeOnStack(_function.localVariables());
+
+ vector<int> stackLayout;
+ stackLayout.push_back(c_returnValuesSize); // target of return address
+ stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
+ for (unsigned i = 0; i < c_returnValuesSize; ++i)
+ stackLayout.push_back(i);
+ stackLayout += vector<int>(c_localVariablesSize, -1);
+
+ solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables.");
+ while (stackLayout.back() != int(stackLayout.size() - 1))
+ if (stackLayout.back() < 0)
+ {
+ m_context << eth::Instruction::POP;
+ stackLayout.pop_back();
+ }
+ else
+ {
+ m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
+ swap(stackLayout[stackLayout.back()], stackLayout.back());
+ }
+ //@todo assert that everything is in place now
+
+ for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
+ m_context.removeVariable(*variable);
+ for (VariableDeclaration const* localVariable: _function.localVariables())
+ m_context.removeVariable(*localVariable);
+
+ m_context.adjustStackOffset(-(int)c_returnValuesSize);
+
+ if (!_function.isConstructor())
+ m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
+ return false;
+}
+
+bool Compiler::visit(IfStatement const& _ifStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
+ compileExpression(_ifStatement.condition());
+ m_context << eth::Instruction::ISZERO;
+ eth::AssemblyItem falseTag = m_context.appendConditionalJump();
+ eth::AssemblyItem endTag = falseTag;
+ _ifStatement.trueStatement().accept(*this);
+ if (_ifStatement.falseStatement())
+ {
+ endTag = m_context.appendJumpToNew();
+ m_context << falseTag;
+ _ifStatement.falseStatement()->accept(*this);
+ }
+ m_context << endTag;
+
+ checker.check();
+ return false;
+}
+
+bool Compiler::visit(WhileStatement const& _whileStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _whileStatement);
+ eth::AssemblyItem loopStart = m_context.newTag();
+ eth::AssemblyItem loopEnd = m_context.newTag();
+ m_continueTags.push_back(loopStart);
+ m_breakTags.push_back(loopEnd);
+
+ m_context << loopStart;
+ compileExpression(_whileStatement.condition());
+ m_context << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(loopEnd);
+
+ _whileStatement.body().accept(*this);
+
+ m_context.appendJumpTo(loopStart);
+ m_context << loopEnd;
+
+ m_continueTags.pop_back();
+ m_breakTags.pop_back();
+
+ checker.check();
+ return false;
+}
+
+bool Compiler::visit(ForStatement const& _forStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _forStatement);
+ eth::AssemblyItem loopStart = m_context.newTag();
+ eth::AssemblyItem loopEnd = m_context.newTag();
+ eth::AssemblyItem loopNext = m_context.newTag();
+ m_continueTags.push_back(loopNext);
+ m_breakTags.push_back(loopEnd);
+
+ if (_forStatement.initializationExpression())
+ _forStatement.initializationExpression()->accept(*this);
+
+ m_context << loopStart;
+
+ // if there is no terminating condition in for, default is to always be true
+ if (_forStatement.condition())
+ {
+ compileExpression(*_forStatement.condition());
+ m_context << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(loopEnd);
+ }
+
+ _forStatement.body().accept(*this);
+
+ m_context << loopNext;
+
+ // for's loop expression if existing
+ if (_forStatement.loopExpression())
+ _forStatement.loopExpression()->accept(*this);
+
+ m_context.appendJumpTo(loopStart);
+ m_context << loopEnd;
+
+ m_continueTags.pop_back();
+ m_breakTags.pop_back();
+
+ checker.check();
+ return false;
+}
+
+bool Compiler::visit(Continue const& _continueStatement)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _continueStatement);
+ if (!m_continueTags.empty())
+ m_context.appendJumpTo(m_continueTags.back());
+ return false;
+}
+
+bool Compiler::visit(Break const& _breakStatement)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _breakStatement);
+ if (!m_breakTags.empty())
+ m_context.appendJumpTo(m_breakTags.back());
+ return false;
+}
+
+bool Compiler::visit(Return const& _return)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _return);
+ if (Expression const* expression = _return.expression())
+ {
+ solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer.");
+ vector<ASTPointer<VariableDeclaration>> const& returnParameters =
+ _return.annotation().functionReturnParameters->parameters();
+ TypePointers types;
+ for (auto const& retVariable: returnParameters)
+ types.push_back(retVariable->annotation().type);
+
+ TypePointer expectedType;
+ if (expression->annotation().type->category() == Type::Category::Tuple || types.size() != 1)
+ expectedType = make_shared<TupleType>(types);
+ else
+ expectedType = types.front();
+ compileExpression(*expression, expectedType);
+
+ for (auto const& retVariable: boost::adaptors::reverse(returnParameters))
+ CompilerUtils(m_context).moveToStackVariable(*retVariable);
+ }
+ for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
+ m_context << eth::Instruction::POP;
+ m_context.appendJumpTo(m_returnTag);
+ m_context.adjustStackOffset(m_stackCleanupForReturn);
+ return false;
+}
+
+bool Compiler::visit(Throw const& _throw)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _throw);
+ m_context.appendJumpTo(m_context.errorTag());
+ return false;
+}
+
+bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
+ if (Expression const* expression = _variableDeclarationStatement.initialValue())
+ {
+ CompilerUtils utils(m_context);
+ compileExpression(*expression);
+ TypePointers valueTypes;
+ if (auto tupleType = dynamic_cast<TupleType const*>(expression->annotation().type.get()))
+ valueTypes = tupleType->components();
+ else
+ valueTypes = TypePointers{expression->annotation().type};
+ auto const& assignments = _variableDeclarationStatement.annotation().assignments;
+ solAssert(assignments.size() == valueTypes.size(), "");
+ for (size_t i = 0; i < assignments.size(); ++i)
+ {
+ size_t j = assignments.size() - i - 1;
+ solAssert(!!valueTypes[j], "");
+ VariableDeclaration const* varDecl = assignments[j];
+ if (!varDecl)
+ utils.popStackElement(*valueTypes[j]);
+ else
+ {
+ utils.convertType(*valueTypes[j], *varDecl->annotation().type);
+ utils.moveToStackVariable(*varDecl);
+ }
+ }
+ }
+ checker.check();
+ return false;
+}
+
+bool Compiler::visit(ExpressionStatement const& _expressionStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement);
+ Expression const& expression = _expressionStatement.expression();
+ compileExpression(expression);
+ CompilerUtils(m_context).popStackElement(*expression.annotation().type);
+ checker.check();
+ return false;
+}
+
+bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
+{
+ StackHeightChecker checker(m_context);
+ CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
+ ++m_modifierDepth;
+ appendModifierOrFunctionCode();
+ --m_modifierDepth;
+ checker.check();
+ return true;
+}
+
+void Compiler::appendFunctionsWithoutCode()
+{
+ set<Declaration const*> functions = m_context.functionsWithoutCode();
+ while (!functions.empty())
+ {
+ for (Declaration const* function: functions)
+ {
+ m_context.setStackOffset(0);
+ function->accept(*this);
+ }
+ functions = m_context.functionsWithoutCode();
+ }
+}
+
+void Compiler::appendModifierOrFunctionCode()
+{
+ solAssert(m_currentFunction, "");
+ if (m_modifierDepth >= m_currentFunction->modifiers().size())
+ m_currentFunction->body().accept(*this);
+ else
+ {
+ ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth];
+
+ // constructor call should be excluded
+ if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration))
+ {
+ ++m_modifierDepth;
+ appendModifierOrFunctionCode();
+ --m_modifierDepth;
+ return;
+ }
+
+ ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name());
+ CompilerContext::LocationSetter locationSetter(m_context, modifier);
+ solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), "");
+ for (unsigned i = 0; i < modifier.parameters().size(); ++i)
+ {
+ m_context.addVariable(*modifier.parameters()[i]);
+ compileExpression(
+ *modifierInvocation->arguments()[i],
+ modifier.parameters()[i]->annotation().type
+ );
+ }
+ for (VariableDeclaration const* localVariable: modifier.localVariables())
+ appendStackVariableInitialisation(*localVariable);
+
+ unsigned const c_stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()) +
+ CompilerUtils::sizeOnStack(modifier.localVariables());
+ m_stackCleanupForReturn += c_stackSurplus;
+
+ modifier.body().accept(*this);
+
+ for (unsigned i = 0; i < c_stackSurplus; ++i)
+ m_context << eth::Instruction::POP;
+ m_stackCleanupForReturn -= c_stackSurplus;
+ }
+}
+
+void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _variable)
+{
+ CompilerContext::LocationSetter location(m_context, _variable);
+ m_context.addVariable(_variable);
+ CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type);
+}
+
+void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
+{
+ ExpressionCompiler expressionCompiler(m_context, m_optimize);
+ expressionCompiler.compile(_expression);
+ if (_targetType)
+ CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
+}
+
+eth::Assembly Compiler::cloneRuntime()
+{
+ eth::EVMSchedule schedule;
+ eth::Assembly a;
+ a << eth::Instruction::CALLDATASIZE;
+ a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY;
+ //@todo adjust for larger return values, make this dynamic.
+ a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE;
+ a << u256(0);
+ // this is the address which has to be substituted by the linker.
+ //@todo implement as special "marker" AssemblyItem.
+ a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
+ a << u256(schedule.callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB;
+ a << eth::Instruction::DELEGATECALL;
+ //Propagate error condition (if DELEGATECALL pushes 0 on stack).
+ a << eth::Instruction::ISZERO;
+ a.appendJumpI(a.errorTag());
+ //@todo adjust for larger return values, make this dynamic.
+ a << u256(0x20) << u256(0) << eth::Instruction::RETURN;
+ return a;
+}
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
new file mode 100644
index 00000000..fa33bd30
--- /dev/null
+++ b/libsolidity/codegen/Compiler.h
@@ -0,0 +1,137 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity AST to EVM bytecode compiler.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <functional>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libevmasm/Assembly.h>
+
+namespace dev {
+namespace solidity {
+
+class Compiler: private ASTConstVisitor
+{
+public:
+ explicit Compiler(bool _optimize = false, unsigned _runs = 200):
+ m_optimize(_optimize),
+ m_optimizeRuns(_runs),
+ m_returnTag(m_context.newTag())
+ { }
+
+ void compileContract(
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+ );
+ /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given
+ /// contract at runtime, but contains the full creation-time code.
+ void compileClone(
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+ );
+ eth::Assembly const& assembly() { return m_context.assembly(); }
+ eth::LinkerObject assembledObject() { return m_context.assembledObject(); }
+ eth::LinkerObject runtimeObject() { return m_context.assembledRuntimeObject(m_runtimeSub); }
+ /// @arg _sourceCodes is the map of input files to source code strings
+ /// @arg _inJsonFromat shows whether the out should be in Json format
+ Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
+ {
+ return m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat);
+ }
+ /// @returns Assembly items of the normal compiler context
+ eth::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); }
+ /// @returns Assembly items of the runtime compiler context
+ eth::AssemblyItems const& runtimeAssemblyItems() const { return m_context.assembly().sub(m_runtimeSub).items(); }
+
+ /// @returns the entry label of the given function. Might return an AssemblyItem of type
+ /// UndefinedItem if it does not exist yet.
+ eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
+
+private:
+ /// Registers the non-function objects inside the contract with the context and stores the basic
+ /// information about the contract like the AST annotations.
+ void initializeContext(
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
+ );
+ /// Adds the code that is run at creation time. Should be run after exchanging the run-time context
+ /// with a new and initialized context. Adds the constructor code.
+ void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
+ /// Appends state variable initialisation and constructor code.
+ void appendInitAndConstructorCode(ContractDefinition const& _contract);
+ void appendBaseConstructor(FunctionDefinition const& _constructor);
+ void appendConstructor(FunctionDefinition const& _constructor);
+ void appendFunctionSelector(ContractDefinition const& _contract);
+ /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
+ /// From memory if @a _fromMemory is true, otherwise from call data.
+ /// Expects source offset on the stack, which is removed.
+ void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
+ void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary);
+
+ void registerStateVariables(ContractDefinition const& _contract);
+ void initializeStateVariables(ContractDefinition const& _contract);
+
+ virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+ virtual bool visit(IfStatement const& _ifStatement) override;
+ virtual bool visit(WhileStatement const& _whileStatement) override;
+ virtual bool visit(ForStatement const& _forStatement) override;
+ virtual bool visit(Continue const& _continue) override;
+ virtual bool visit(Break const& _break) override;
+ virtual bool visit(Return const& _return) override;
+ virtual bool visit(Throw const& _throw) override;
+ virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
+ virtual bool visit(ExpressionStatement const& _expressionStatement) override;
+ virtual bool visit(PlaceholderStatement const&) override;
+
+ /// Repeatedly visits all function which are referenced but which are not compiled yet.
+ void appendFunctionsWithoutCode();
+
+ /// Appends one layer of function modifier code of the current function, or the function
+ /// body itself if the last modifier was reached.
+ void appendModifierOrFunctionCode();
+
+ void appendStackVariableInitialisation(VariableDeclaration const& _variable);
+ void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
+
+ /// @returns the runtime assembly for clone contracts.
+ static eth::Assembly cloneRuntime();
+
+ bool const m_optimize;
+ unsigned const m_optimizeRuns;
+ CompilerContext m_context;
+ size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly
+ CompilerContext m_runtimeContext;
+ std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
+ std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
+ eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
+ unsigned m_modifierDepth = 0;
+ FunctionDefinition const* m_currentFunction = nullptr;
+ unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
+ // arguments for base constructors, filled in derived-to-base order
+ std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
+};
+
+}
+}
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
new file mode 100644
index 00000000..9e2405cc
--- /dev/null
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -0,0 +1,223 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Utilities for the solidity compiler.
+ */
+
+#include <libsolidity/codegen/CompilerContext.h>
+#include <utility>
+#include <numeric>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/interface/Version.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
+{
+ m_magicGlobals.insert(&_declaration);
+}
+
+void CompilerContext::addStateVariable(
+ VariableDeclaration const& _declaration,
+ u256 const& _storageOffset,
+ unsigned _byteOffset
+)
+{
+ m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset);
+}
+
+void CompilerContext::startFunction(Declaration const& _function)
+{
+ m_functionsWithCode.insert(&_function);
+ *this << functionEntryLabel(_function);
+}
+
+void CompilerContext::addVariable(VariableDeclaration const& _declaration,
+ unsigned _offsetToCurrent)
+{
+ solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
+ m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
+}
+
+void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
+{
+ solAssert(!!m_localVariables.count(&_declaration), "");
+ m_localVariables.erase(&_declaration);
+}
+
+eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
+{
+ auto ret = m_compiledContracts.find(&_contract);
+ solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
+ return *ret->second;
+}
+
+bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
+{
+ return !!m_localVariables.count(_declaration);
+}
+
+eth::AssemblyItem CompilerContext::functionEntryLabel(Declaration const& _declaration)
+{
+ auto res = m_functionEntryLabels.find(&_declaration);
+ if (res == m_functionEntryLabels.end())
+ {
+ eth::AssemblyItem tag(m_asm.newTag());
+ m_functionEntryLabels.insert(make_pair(&_declaration, tag));
+ return tag.tag();
+ }
+ else
+ return res->second.tag();
+}
+
+eth::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const& _declaration) const
+{
+ auto res = m_functionEntryLabels.find(&_declaration);
+ return res == m_functionEntryLabels.end() ? eth::AssemblyItem(eth::UndefinedItem) : res->second.tag();
+}
+
+eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(FunctionDefinition const& _function)
+{
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ return virtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin());
+}
+
+eth::AssemblyItem CompilerContext::superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base)
+{
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ return virtualFunctionEntryLabel(_function, superContract(_base));
+}
+
+FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const
+{
+ vector<ContractDefinition const*>::const_iterator it = superContract(_contract);
+ for (; it != m_inheritanceHierarchy.end(); ++it)
+ if ((*it)->constructor())
+ return (*it)->constructor();
+
+ return nullptr;
+}
+
+set<Declaration const*> CompilerContext::functionsWithoutCode()
+{
+ set<Declaration const*> functions;
+ for (auto const& it: m_functionEntryLabels)
+ if (m_functionsWithCode.count(it.first) == 0)
+ functions.insert(it.first);
+ return functions;
+}
+
+ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const
+{
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ for (ContractDefinition const* contract: m_inheritanceHierarchy)
+ for (ModifierDefinition const* modifier: contract->functionModifiers())
+ if (modifier->name() == _name)
+ return *modifier;
+ BOOST_THROW_EXCEPTION(InternalCompilerError()
+ << errinfo_comment("Function modifier " + _name + " not found."));
+}
+
+unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const
+{
+ auto res = m_localVariables.find(&_declaration);
+ solAssert(res != m_localVariables.end(), "Variable not found on stack.");
+ return res->second;
+}
+
+unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
+{
+ return m_asm.deposit() - _baseOffset - 1;
+}
+
+unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
+{
+ return m_asm.deposit() - _offset - 1;
+}
+
+pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const
+{
+ auto it = m_stateVariables.find(&_declaration);
+ solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
+ return it->second;
+}
+
+CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType)
+{
+ eth::AssemblyItem item(eth::Instruction::JUMP);
+ item.setJumpType(_jumpType);
+ return *this << item;
+}
+
+void CompilerContext::resetVisitedNodes(ASTNode const* _node)
+{
+ stack<ASTNode const*> newStack;
+ newStack.push(_node);
+ std::swap(m_visitedNodes, newStack);
+ updateSourceLocation();
+}
+
+void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
+{
+ eth::Assembly& sub = m_asm.sub(_subIndex);
+ sub.injectStart(eth::Instruction::POP);
+ sub.injectStart(fromBigEndian<u256>(binaryVersion()));
+}
+
+eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
+ FunctionDefinition const& _function,
+ vector<ContractDefinition const*>::const_iterator _searchStart
+)
+{
+ string name = _function.name();
+ FunctionType functionType(_function);
+ auto it = _searchStart;
+ for (; it != m_inheritanceHierarchy.end(); ++it)
+ for (FunctionDefinition const* function: (*it)->definedFunctions())
+ if (
+ function->name() == name &&
+ !function->isConstructor() &&
+ FunctionType(*function).hasEqualArgumentTypes(functionType)
+ )
+ return functionEntryLabel(*function);
+ solAssert(false, "Super function " + name + " not found.");
+ return m_asm.newTag(); // not reached
+}
+
+vector<ContractDefinition const*>::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const
+{
+ solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
+ auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_contract);
+ solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy.");
+ return ++it;
+}
+
+void CompilerContext::updateSourceLocation()
+{
+ m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
+}
+
+}
+}
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
new file mode 100644
index 00000000..5287088a
--- /dev/null
+++ b/libsolidity/codegen/CompilerContext.h
@@ -0,0 +1,189 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Utilities for the solidity compiler.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <stack>
+#include <utility>
+#include <libevmcore/Instruction.h>
+#include <libevmasm/Assembly.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+#include <libdevcore/Common.h>
+
+namespace dev {
+namespace solidity {
+
+
+/**
+ * Context to be shared by all units that compile the same contract.
+ * It stores the generated bytecode and the position of identifiers in memory and on the stack.
+ */
+class CompilerContext
+{
+public:
+ void addMagicGlobal(MagicVariableDeclaration const& _declaration);
+ void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
+ void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
+ void removeVariable(VariableDeclaration const& _declaration);
+
+ void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
+ eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
+
+ void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
+ void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
+ unsigned stackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
+
+ bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
+ bool isLocalVariable(Declaration const* _declaration) const;
+ bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
+
+ /// @returns the entry label of the given function and creates it if it does not exist yet.
+ eth::AssemblyItem functionEntryLabel(Declaration const& _declaration);
+ /// @returns the entry label of the given function. Might return an AssemblyItem of type
+ /// UndefinedItem if it does not exist yet.
+ eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
+ void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
+ /// @returns the entry label of the given function and takes overrides into account.
+ eth::AssemblyItem virtualFunctionEntryLabel(FunctionDefinition const& _function);
+ /// @returns the entry label of a function that overrides the given declaration from the most derived class just
+ /// above _base in the current inheritance hierarchy.
+ eth::AssemblyItem superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base);
+ FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const;
+
+ /// @returns the set of functions for which we still need to generate code
+ std::set<Declaration const*> functionsWithoutCode();
+ /// Resets function specific members, inserts the function entry label and marks the function
+ /// as "having code".
+ void startFunction(Declaration const& _function);
+
+ ModifierDefinition const& functionModifier(std::string const& _name) const;
+ /// Returns the distance of the given local variable from the bottom of the stack (of the current function).
+ unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
+ /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns
+ /// the distance of that variable from the current top of the stack.
+ unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
+ /// Converts an offset relative to the current stack height to a value that can be used later
+ /// with baseToCurrentStackOffset to point to the same stack element.
+ unsigned currentToBaseStackOffset(unsigned _offset) const;
+ /// @returns pair of slot and byte offset of the value inside this slot.
+ std::pair<u256, unsigned> storageLocationOfVariable(Declaration const& _declaration) const;
+
+ /// Appends a JUMPI instruction to a new tag and @returns the tag
+ eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
+ /// Appends a JUMPI instruction to @a _tag
+ CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
+ /// Appends a JUMP to a new tag and @returns the tag
+ eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
+ /// Appends a JUMP to a tag already on the stack
+ CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
+ /// Returns an "ErrorTag"
+ eth::AssemblyItem errorTag() { return m_asm.errorTag(); }
+ /// Appends a JUMP to a specific tag
+ CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
+ /// Appends pushing of a new tag and @returns the new tag.
+ eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
+ /// @returns a new tag without pushing any opcodes or data
+ eth::AssemblyItem newTag() { return m_asm.newTag(); }
+ /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
+ /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
+ eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
+ /// Pushes the size of the final program
+ void appendProgramSize() { return m_asm.appendProgramSize(); }
+ /// Adds data to the data section, pushes a reference to the stack
+ eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
+ /// Appends the address (virtual, will be filled in by linker) of a library.
+ void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); }
+ /// Resets the stack of visited nodes with a new stack having only @c _node
+ void resetVisitedNodes(ASTNode const* _node);
+ /// Pops the stack of visited nodes
+ void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); }
+ /// Pushes an ASTNode to the stack of visited nodes
+ void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
+
+ /// Append elements to the current instruction list and adjust @a m_stackOffset.
+ CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
+ CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
+ CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
+ CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
+
+ /// Prepends "PUSH <compiler version number> POP"
+ void injectVersionStampIntoSub(size_t _subIndex);
+
+ void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
+
+ eth::Assembly const& assembly() const { return m_asm; }
+ /// @arg _sourceCodes is the map of input files to source code strings
+ /// @arg _inJsonFormat shows whether the out should be in Json format
+ Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
+ {
+ return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
+ }
+
+ eth::LinkerObject const& assembledObject() { return m_asm.assemble(); }
+ eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm.sub(_subIndex).assemble(); }
+
+ /**
+ * Helper class to pop the visited nodes stack when a scope closes
+ */
+ class LocationSetter: public ScopeGuard
+ {
+ public:
+ LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node):
+ ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); }
+ };
+
+private:
+ /// @returns the entry label of the given function - searches the inheritance hierarchy
+ /// startig from the given point towards the base.
+ eth::AssemblyItem virtualFunctionEntryLabel(
+ FunctionDefinition const& _function,
+ std::vector<ContractDefinition const*>::const_iterator _searchStart
+ );
+ /// @returns an iterator to the contract directly above the given contract.
+ std::vector<ContractDefinition const*>::const_iterator superContract(const ContractDefinition &_contract) const;
+ /// Updates source location set in the assembly.
+ void updateSourceLocation();
+
+ eth::Assembly m_asm;
+ /// Magic global variables like msg, tx or this, distinguished by type.
+ std::set<Declaration const*> m_magicGlobals;
+ /// Other already compiled contracts to be used in contract creation calls.
+ std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
+ /// Storage offsets of state variables
+ std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
+ /// Offsets of local variables on the stack (relative to stack base).
+ std::map<Declaration const*, unsigned> m_localVariables;
+ /// Labels pointing to the entry points of functions.
+ std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
+ /// Set of functions for which we did not yet generate code.
+ std::set<Declaration const*> m_functionsWithCode;
+ /// List of current inheritance hierarchy from derived to base.
+ std::vector<ContractDefinition const*> m_inheritanceHierarchy;
+ /// Stack of current visited AST nodes, used for location attachment
+ std::stack<ASTNode const*> m_visitedNodes;
+};
+
+}
+}
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
new file mode 100644
index 00000000..bd0857f6
--- /dev/null
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -0,0 +1,822 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Routines used by both the compiler and the expression compiler.
+ */
+
+#include <libsolidity/codegen/CompilerUtils.h>
+#include <libsolidity/ast/AST.h>
+#include <libevmcore/Instruction.h>
+#include <libsolidity/codegen/ArrayUtils.h>
+#include <libsolidity/codegen/LValue.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+const unsigned CompilerUtils::dataStartOffset = 4;
+const size_t CompilerUtils::freeMemoryPointer = 64;
+const unsigned CompilerUtils::identityContractAddress = 4;
+
+void CompilerUtils::initialiseFreeMemoryPointer()
+{
+ m_context << u256(freeMemoryPointer + 32);
+ storeFreeMemoryPointer();
+}
+
+void CompilerUtils::fetchFreeMemoryPointer()
+{
+ m_context << u256(freeMemoryPointer) << eth::Instruction::MLOAD;
+}
+
+void CompilerUtils::storeFreeMemoryPointer()
+{
+ m_context << u256(freeMemoryPointer) << eth::Instruction::MSTORE;
+}
+
+void CompilerUtils::allocateMemory()
+{
+ fetchFreeMemoryPointer();
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD;
+ storeFreeMemoryPointer();
+}
+
+void CompilerUtils::toSizeAfterFreeMemoryPointer()
+{
+ fetchFreeMemoryPointer();
+ m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2 << eth::Instruction::SUB;
+ m_context << eth::Instruction::SWAP1;
+}
+
+unsigned CompilerUtils::loadFromMemory(
+ unsigned _offset,
+ Type const& _type,
+ bool _fromCalldata,
+ bool _padToWordBoundaries
+)
+{
+ solAssert(_type.category() != Type::Category::Array, "Unable to statically load dynamic type.");
+ m_context << u256(_offset);
+ return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
+}
+
+void CompilerUtils::loadFromMemoryDynamic(
+ Type const& _type,
+ bool _fromCalldata,
+ bool _padToWordBoundaries,
+ bool _keepUpdatedMemoryOffset
+)
+{
+ if (_keepUpdatedMemoryOffset)
+ m_context << eth::Instruction::DUP1;
+
+ if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
+ {
+ solAssert(!arrayType->isDynamicallySized(), "");
+ solAssert(!_fromCalldata, "");
+ solAssert(_padToWordBoundaries, "");
+ if (_keepUpdatedMemoryOffset)
+ m_context << arrayType->memorySize() << eth::Instruction::ADD;
+ }
+ else
+ {
+ unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
+ if (_keepUpdatedMemoryOffset)
+ {
+ // update memory counter
+ moveToStackTop(_type.sizeOnStack());
+ m_context << u256(numBytes) << eth::Instruction::ADD;
+ }
+ }
+}
+
+void CompilerUtils::storeInMemory(unsigned _offset)
+{
+ unsigned numBytes = prepareMemoryStore(IntegerType(256), true);
+ if (numBytes > 0)
+ m_context << u256(_offset) << eth::Instruction::MSTORE;
+}
+
+void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
+{
+ if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
+ {
+ solAssert(ref->location() == DataLocation::Memory, "");
+ storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
+ }
+ else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
+ {
+ m_context << eth::Instruction::DUP1;
+ storeStringData(bytesConstRef(str->value()));
+ if (_padToWordBoundaries)
+ m_context << u256(((str->value().size() + 31) / 32) * 32);
+ else
+ m_context << u256(str->value().size());
+ m_context << eth::Instruction::ADD;
+ }
+ else
+ {
+ unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
+ if (numBytes > 0)
+ {
+ solAssert(
+ _type.sizeOnStack() == 1,
+ "Memory store of types with stack size != 1 not implemented."
+ );
+ m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
+ m_context << u256(numBytes) << eth::Instruction::ADD;
+ }
+ }
+}
+
+void CompilerUtils::encodeToMemory(
+ TypePointers const& _givenTypes,
+ TypePointers const& _targetTypes,
+ bool _padToWordBoundaries,
+ bool _copyDynamicDataInPlace,
+ bool _encodeAsLibraryTypes
+)
+{
+ // stack: <v1> <v2> ... <vn> <mem>
+ TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
+ solAssert(targetTypes.size() == _givenTypes.size(), "");
+ for (TypePointer& t: targetTypes)
+ t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
+
+ // Stack during operation:
+ // <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
+ // The values dyn_head_i are added during the first loop and they point to the head part
+ // of the ith dynamic parameter, which is filled once the dynamic parts are processed.
+
+ // store memory start pointer
+ m_context << eth::Instruction::DUP1;
+
+ unsigned argSize = CompilerUtils::sizeOnStack(_givenTypes);
+ unsigned stackPos = 0; // advances through the argument values
+ unsigned dynPointers = 0; // number of dynamic head pointers on the stack
+ for (size_t i = 0; i < _givenTypes.size(); ++i)
+ {
+ TypePointer targetType = targetTypes[i];
+ solAssert(!!targetType, "Externalable type expected.");
+ if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
+ {
+ // leave end_of_mem as dyn head pointer
+ m_context << eth::Instruction::DUP1 << u256(32) << eth::Instruction::ADD;
+ dynPointers++;
+ }
+ else
+ {
+ copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->sizeOnStack());
+ solAssert(!!targetType, "Externalable type expected.");
+ TypePointer type = targetType;
+ if (_givenTypes[i]->dataStoredIn(DataLocation::Storage) && targetType->isValueType())
+ {
+ // special case: convert storage reference type to value type - this is only
+ // possible for library calls where we just forward the storage reference
+ solAssert(_encodeAsLibraryTypes, "");
+ solAssert(_givenTypes[i]->sizeOnStack() == 1, "");
+ }
+ else if (
+ _givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
+ _givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
+ _givenTypes[i]->category() == Type::Category::StringLiteral
+ )
+ type = _givenTypes[i]; // delay conversion
+ else
+ convertType(*_givenTypes[i], *targetType, true);
+ if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
+ ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
+ else
+ storeInMemoryDynamic(*type, _padToWordBoundaries);
+ }
+ stackPos += _givenTypes[i]->sizeOnStack();
+ }
+
+ // now copy the dynamic part
+ // Stack: <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
+ stackPos = 0;
+ unsigned thisDynPointer = 0;
+ for (size_t i = 0; i < _givenTypes.size(); ++i)
+ {
+ TypePointer targetType = targetTypes[i];
+ solAssert(!!targetType, "Externalable type expected.");
+ if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
+ {
+ // copy tail pointer (=mem_end - mem_start) to memory
+ m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2;
+ m_context << eth::Instruction::SUB;
+ m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer);
+ m_context << eth::Instruction::MSTORE;
+ // stack: ... <end_of_mem>
+ if (_givenTypes[i]->category() == Type::Category::StringLiteral)
+ {
+ auto const& strType = dynamic_cast<StringLiteralType const&>(*_givenTypes[i]);
+ m_context << u256(strType.value().size());
+ storeInMemoryDynamic(IntegerType(256), true);
+ // stack: ... <end_of_mem'>
+ storeInMemoryDynamic(strType, _padToWordBoundaries);
+ }
+ else
+ {
+ solAssert(_givenTypes[i]->category() == Type::Category::Array, "Unknown dynamic type.");
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
+ // now copy the array
+ copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
+ // stack: ... <end_of_mem> <value...>
+ // copy length to memory
+ m_context << eth::dupInstruction(1 + arrayType.sizeOnStack());
+ ArrayUtils(m_context).retrieveLength(arrayType, 1);
+ // stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
+ storeInMemoryDynamic(IntegerType(256), true);
+ // stack: ... <end_of_mem> <value...> <end_of_mem''>
+ // copy the new memory pointer
+ m_context << eth::swapInstruction(arrayType.sizeOnStack() + 1) << eth::Instruction::POP;
+ // stack: ... <end_of_mem''> <value...>
+ // copy data part
+ ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
+ // stack: ... <end_of_mem'''>
+ }
+
+ thisDynPointer++;
+ }
+ stackPos += _givenTypes[i]->sizeOnStack();
+ }
+
+ // remove unneeded stack elements (and retain memory pointer)
+ m_context << eth::swapInstruction(argSize + dynPointers + 1);
+ popStackSlots(argSize + dynPointers + 1);
+}
+
+void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
+{
+ auto repeat = m_context.newTag();
+ m_context << repeat;
+ pushZeroValue(*_type.baseType());
+ storeInMemoryDynamic(*_type.baseType());
+ m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::SWAP1;
+ m_context << eth::Instruction::SUB << eth::Instruction::SWAP1;
+ m_context << eth::Instruction::DUP2;
+ m_context.appendConditionalJumpTo(repeat);
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+}
+
+void CompilerUtils::memoryCopy()
+{
+ // Stack here: size target source
+ // stack for call: outsize target size source value contract gas
+ //@TODO do not use ::CALL if less than 32 bytes?
+ m_context << eth::Instruction::DUP3 << eth::Instruction::SWAP1;
+ m_context << u256(0) << u256(identityContractAddress);
+ // compute gas costs
+ m_context << u256(32) << eth::Instruction::DUP5 << u256(31) << eth::Instruction::ADD;
+ static unsigned c_identityGas = 3;
+ static unsigned c_identityWordGas = 15;
+ m_context << eth::Instruction::DIV << u256(c_identityWordGas) << eth::Instruction::MUL;
+ m_context << u256(c_identityGas) << eth::Instruction::ADD;
+ m_context << eth::Instruction::CALL;
+ m_context << eth::Instruction::POP; // ignore return value
+}
+
+void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
+{
+ // For a type extension, we need to remove all higher-order bits that we might have ignored in
+ // previous operations.
+ // @todo: store in the AST whether the operand might have "dirty" higher order bits
+
+ if (_typeOnStack == _targetType && !_cleanupNeeded)
+ return;
+ Type::Category stackTypeCategory = _typeOnStack.category();
+ Type::Category targetTypeCategory = _targetType.category();
+
+ switch (stackTypeCategory)
+ {
+ case Type::Category::FixedBytes:
+ {
+ FixedBytesType const& typeOnStack = dynamic_cast<FixedBytesType const&>(_typeOnStack);
+ if (targetTypeCategory == Type::Category::Integer)
+ {
+ // conversion from bytes to integer. no need to clean the high bit
+ // only to shift right because of opposite alignment
+ IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
+ m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
+ convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
+ }
+ else
+ {
+ // clear lower-order bytes for conversion to shorter bytes - we always clean
+ solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
+ FixedBytesType const& targetType = dynamic_cast<FixedBytesType const&>(_targetType);
+ if (targetType.numBytes() < typeOnStack.numBytes())
+ {
+ if (targetType.numBytes() == 0)
+ m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
+ else
+ {
+ m_context << (u256(1) << (256 - targetType.numBytes() * 8));
+ m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
+ m_context << eth::Instruction::DIV << eth::Instruction::MUL;
+ }
+ }
+ }
+ }
+ break;
+ case Type::Category::Enum:
+ solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, "");
+ break;
+ case Type::Category::Integer:
+ case Type::Category::Contract:
+ case Type::Category::IntegerConstant:
+ if (targetTypeCategory == Type::Category::FixedBytes)
+ {
+ solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant,
+ "Invalid conversion to FixedBytesType requested.");
+ // conversion from bytes to string. no need to clean the high bit
+ // only to shift left because of opposite alignment
+ FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
+ if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
+ if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
+ cleanHigherOrderBits(*typeOnStack);
+ m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << eth::Instruction::MUL;
+ }
+ else if (targetTypeCategory == Type::Category::Enum)
+ // just clean
+ convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
+ else
+ {
+ solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
+ IntegerType addressType(0, IntegerType::Modifier::Address);
+ IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
+ ? dynamic_cast<IntegerType const&>(_targetType) : addressType;
+ if (stackTypeCategory == Type::Category::IntegerConstant)
+ {
+ IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
+ // We know that the stack is clean, we only have to clean for a narrowing conversion
+ // where cleanup is forced.
+ if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded)
+ cleanHigherOrderBits(targetType);
+ }
+ else
+ {
+ IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
+ ? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
+ // Widening: clean up according to source type width
+ // Non-widening and force: clean up according to target type bits
+ if (targetType.numBits() > typeOnStack.numBits())
+ cleanHigherOrderBits(typeOnStack);
+ else if (_cleanupNeeded)
+ cleanHigherOrderBits(targetType);
+ }
+ }
+ break;
+ case Type::Category::StringLiteral:
+ {
+ auto const& literalType = dynamic_cast<StringLiteralType const&>(_typeOnStack);
+ string const& value = literalType.value();
+ bytesConstRef data(value);
+ if (targetTypeCategory == Type::Category::FixedBytes)
+ {
+ solAssert(data.size() <= 32, "");
+ m_context << h256::Arith(h256(data, h256::AlignLeft));
+ }
+ else if (targetTypeCategory == Type::Category::Array)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
+ solAssert(arrayType.isByteArray(), "");
+ u256 storageSize(32 + ((data.size() + 31) / 32) * 32);
+ m_context << storageSize;
+ allocateMemory();
+ // stack: mempos
+ m_context << eth::Instruction::DUP1 << u256(data.size());
+ storeInMemoryDynamic(IntegerType(256));
+ // stack: mempos datapos
+ storeStringData(data);
+ break;
+ }
+ else
+ solAssert(
+ false,
+ "Invalid conversion from string literal to " + _targetType.toString(false) + " requested."
+ );
+ break;
+ }
+ case Type::Category::Array:
+ {
+ solAssert(targetTypeCategory == stackTypeCategory, "");
+ ArrayType const& typeOnStack = dynamic_cast<ArrayType const&>(_typeOnStack);
+ ArrayType const& targetType = dynamic_cast<ArrayType const&>(_targetType);
+ switch (targetType.location())
+ {
+ case DataLocation::Storage:
+ // Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
+ solAssert(
+ (targetType.isPointer() || (typeOnStack.isByteArray() && targetType.isByteArray())) &&
+ typeOnStack.location() == DataLocation::Storage,
+ "Invalid conversion to storage type."
+ );
+ break;
+ case DataLocation::Memory:
+ {
+ // Copy the array to a free position in memory, unless it is already in memory.
+ if (typeOnStack.location() != DataLocation::Memory)
+ {
+ // stack: <source ref> (variably sized)
+ unsigned stackSize = typeOnStack.sizeOnStack();
+ ArrayUtils(m_context).retrieveLength(typeOnStack);
+
+ // allocate memory
+ // stack: <source ref> (variably sized) <length>
+ m_context << eth::Instruction::DUP1;
+ ArrayUtils(m_context).convertLengthToSize(targetType, true);
+ // stack: <source ref> (variably sized) <length> <size>
+ if (targetType.isDynamicallySized())
+ m_context << u256(0x20) << eth::Instruction::ADD;
+ allocateMemory();
+ // stack: <source ref> (variably sized) <length> <mem start>
+ m_context << eth::Instruction::DUP1;
+ moveIntoStack(2 + stackSize);
+ if (targetType.isDynamicallySized())
+ {
+ m_context << eth::Instruction::DUP2;
+ storeInMemoryDynamic(IntegerType(256));
+ }
+ // stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
+ if (targetType.baseType()->isValueType())
+ {
+ solAssert(typeOnStack.baseType()->isValueType(), "");
+ copyToStackTop(2 + stackSize, stackSize);
+ ArrayUtils(m_context).copyArrayToMemory(typeOnStack);
+ }
+ else
+ {
+ m_context << u256(0) << eth::Instruction::SWAP1;
+ // stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
+ auto repeat = m_context.newTag();
+ m_context << repeat;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
+ m_context << eth::Instruction::LT << eth::Instruction::ISZERO;
+ auto loopEnd = m_context.appendConditionalJump();
+ copyToStackTop(3 + stackSize, stackSize);
+ copyToStackTop(2 + stackSize, 1);
+ ArrayUtils(m_context).accessIndex(typeOnStack, false);
+ if (typeOnStack.location() == DataLocation::Storage)
+ StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
+ convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
+ storeInMemoryDynamic(*targetType.baseType(), true);
+ m_context << eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP1;
+ m_context.appendJumpTo(repeat);
+ m_context << loopEnd;
+ m_context << eth::Instruction::POP;
+ }
+ // stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
+ popStackSlots(2 + stackSize);
+ // Stack: <mem start>
+ }
+ break;
+ }
+ case DataLocation::CallData:
+ solAssert(
+ targetType.isByteArray() &&
+ typeOnStack.isByteArray() &&
+ typeOnStack.location() == DataLocation::CallData,
+ "Invalid conversion to calldata type.");
+ break;
+ default:
+ solAssert(
+ false,
+ "Invalid type conversion " +
+ _typeOnStack.toString(false) +
+ " to " +
+ _targetType.toString(false) +
+ " requested."
+ );
+ }
+ break;
+ }
+ case Type::Category::Struct:
+ {
+ solAssert(targetTypeCategory == stackTypeCategory, "");
+ auto& targetType = dynamic_cast<StructType const&>(_targetType);
+ auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack);
+ solAssert(
+ targetType.location() != DataLocation::CallData &&
+ typeOnStack.location() != DataLocation::CallData
+ , "");
+ switch (targetType.location())
+ {
+ case DataLocation::Storage:
+ // Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
+ solAssert(
+ targetType.isPointer() &&
+ typeOnStack.location() == DataLocation::Storage,
+ "Invalid conversion to storage type."
+ );
+ break;
+ case DataLocation::Memory:
+ // Copy the array to a free position in memory, unless it is already in memory.
+ if (typeOnStack.location() != DataLocation::Memory)
+ {
+ solAssert(typeOnStack.location() == DataLocation::Storage, "");
+ // stack: <source ref>
+ m_context << typeOnStack.memorySize();
+ allocateMemory();
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
+ // stack: <memory ptr> <source ref> <memory ptr>
+ for (auto const& member: typeOnStack.members(nullptr))
+ {
+ if (!member.type->canLiveOutsideStorage())
+ continue;
+ pair<u256, unsigned> const& offsets = typeOnStack.storageOffsetsOfMember(member.name);
+ m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << u256(offsets.second);
+ StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true);
+ TypePointer targetMemberType = targetType.memberType(member.name);
+ solAssert(!!targetMemberType, "Member not found in target type.");
+ convertType(*member.type, *targetMemberType, true);
+ storeInMemoryDynamic(*targetMemberType, true);
+ }
+ m_context << eth::Instruction::POP << eth::Instruction::POP;
+ }
+ break;
+ case DataLocation::CallData:
+ solAssert(false, "Invalid type conversion target location CallData.");
+ break;
+ }
+ break;
+ }
+ case Type::Category::Tuple:
+ {
+ TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack);
+ TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType);
+ // fillRight: remove excess values at right side, !fillRight: remove eccess values at left side
+ bool fillRight = !targetTuple.components().empty() && (
+ !targetTuple.components().back() ||
+ targetTuple.components().front()
+ );
+ unsigned depth = sourceTuple.sizeOnStack();
+ for (size_t i = 0; i < sourceTuple.components().size(); ++i)
+ {
+ TypePointer sourceType = sourceTuple.components()[i];
+ TypePointer targetType;
+ if (fillRight && i < targetTuple.components().size())
+ targetType = targetTuple.components()[i];
+ else if (!fillRight && targetTuple.components().size() + i >= sourceTuple.components().size())
+ targetType = targetTuple.components()[targetTuple.components().size() - (sourceTuple.components().size() - i)];
+ if (!sourceType)
+ {
+ solAssert(!targetType, "");
+ continue;
+ }
+ unsigned sourceSize = sourceType->sizeOnStack();
+ unsigned targetSize = targetType ? targetType->sizeOnStack() : 0;
+ if (!targetType || *sourceType != *targetType || _cleanupNeeded)
+ {
+ if (targetType)
+ {
+ if (sourceSize > 0)
+ copyToStackTop(depth, sourceSize);
+ convertType(*sourceType, *targetType, _cleanupNeeded);
+ }
+ if (sourceSize > 0 || targetSize > 0)
+ {
+ // Move it back into its place.
+ for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
+ m_context <<
+ eth::swapInstruction(depth + targetSize - sourceSize) <<
+ eth::Instruction::POP;
+ // Value shrank
+ for (unsigned j = targetSize; j < sourceSize; ++j)
+ {
+ moveToStackTop(depth - 1, 1);
+ m_context << eth::Instruction::POP;
+ }
+ // Value grew
+ if (targetSize > sourceSize)
+ moveIntoStack(depth + targetSize - sourceSize - 1, targetSize - sourceSize);
+ }
+ }
+ depth -= sourceSize;
+ }
+ break;
+ }
+ default:
+ // All other types should not be convertible to non-equal types.
+ solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
+ break;
+ }
+}
+
+void CompilerUtils::pushZeroValue(Type const& _type)
+{
+ auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
+ if (!referenceType || referenceType->location() == DataLocation::Storage)
+ {
+ for (size_t i = 0; i < _type.sizeOnStack(); ++i)
+ m_context << u256(0);
+ return;
+ }
+ solAssert(referenceType->location() == DataLocation::Memory, "");
+
+ m_context << u256(max(32u, _type.calldataEncodedSize()));
+ allocateMemory();
+ m_context << eth::Instruction::DUP1;
+
+ if (auto structType = dynamic_cast<StructType const*>(&_type))
+ for (auto const& member: structType->members(nullptr))
+ {
+ pushZeroValue(*member.type);
+ storeInMemoryDynamic(*member.type);
+ }
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
+ {
+ if (arrayType->isDynamicallySized())
+ {
+ // zero length
+ m_context << u256(0);
+ storeInMemoryDynamic(IntegerType(256));
+ }
+ else if (arrayType->length() > 0)
+ {
+ m_context << arrayType->length() << eth::Instruction::SWAP1;
+ // stack: items_to_do memory_pos
+ zeroInitialiseMemoryArray(*arrayType);
+ // stack: updated_memory_pos
+ }
+ }
+ else
+ solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
+
+ // remove the updated memory pointer
+ m_context << eth::Instruction::POP;
+}
+
+void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
+{
+ unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
+ unsigned const size = _variable.annotation().type->sizeOnStack();
+ solAssert(stackPosition >= size, "Variable size and position mismatch.");
+ // move variable starting from its top end in the stack
+ if (stackPosition - size + 1 > 16)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_variable.location()) <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ for (unsigned i = 0; i < size; ++i)
+ m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
+}
+
+void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
+{
+ solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
+ for (unsigned i = 0; i < _itemSize; ++i)
+ m_context << eth::dupInstruction(_stackDepth);
+}
+
+void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
+{
+ moveIntoStack(_itemSize, _stackDepth);
+}
+
+void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
+{
+ if (_stackDepth <= _itemSize)
+ for (unsigned i = 0; i < _stackDepth; ++i)
+ rotateStackDown(_stackDepth + _itemSize);
+ else
+ for (unsigned i = 0; i < _itemSize; ++i)
+ rotateStackUp(_stackDepth + _itemSize);
+}
+
+void CompilerUtils::rotateStackUp(unsigned _items)
+{
+ solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
+ for (unsigned i = 1; i < _items; ++i)
+ m_context << eth::swapInstruction(_items - i);
+}
+
+void CompilerUtils::rotateStackDown(unsigned _items)
+{
+ solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
+ for (unsigned i = 1; i < _items; ++i)
+ m_context << eth::swapInstruction(i);
+}
+
+void CompilerUtils::popStackElement(Type const& _type)
+{
+ popStackSlots(_type.sizeOnStack());
+}
+
+void CompilerUtils::popStackSlots(size_t _amount)
+{
+ for (size_t i = 0; i < _amount; ++i)
+ m_context << eth::Instruction::POP;
+}
+
+unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
+{
+ unsigned size = 0;
+ for (shared_ptr<Type const> const& type: _variableTypes)
+ size += type->sizeOnStack();
+ return size;
+}
+
+void CompilerUtils::computeHashStatic()
+{
+ storeInMemory(0);
+ m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
+}
+
+void CompilerUtils::storeStringData(bytesConstRef _data)
+{
+ //@todo provide both alternatives to the optimiser
+ // stack: mempos
+ if (_data.size() <= 128)
+ {
+ for (unsigned i = 0; i < _data.size(); i += 32)
+ {
+ m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft));
+ storeInMemoryDynamic(IntegerType(256));
+ }
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ // stack: mempos mempos_data
+ m_context.appendData(_data.toBytes());
+ m_context << u256(_data.size()) << eth::Instruction::SWAP2;
+ m_context << eth::Instruction::CODECOPY;
+ }
+}
+
+unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
+{
+ unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
+ bool leftAligned = _type.category() == Type::Category::FixedBytes;
+ if (numBytes == 0)
+ m_context << eth::Instruction::POP << u256(0);
+ else
+ {
+ solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
+ m_context << (_fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD);
+ if (numBytes != 32)
+ {
+ // add leading or trailing zeros by dividing/multiplying depending on alignment
+ u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
+ m_context << shiftFactor << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ if (leftAligned)
+ m_context << shiftFactor << eth::Instruction::MUL;
+ }
+ }
+
+ return numBytes;
+}
+
+void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
+{
+ if (_typeOnStack.numBits() == 256)
+ return;
+ else if (_typeOnStack.isSigned())
+ m_context << u256(_typeOnStack.numBits() / 8 - 1) << eth::Instruction::SIGNEXTEND;
+ else
+ m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << eth::Instruction::AND;
+}
+
+unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
+{
+ unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
+ bool leftAligned = _type.category() == Type::Category::FixedBytes;
+ if (numBytes == 0)
+ m_context << eth::Instruction::POP;
+ else
+ {
+ solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
+ if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
+ // shift the value accordingly before storing
+ m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
+ }
+ return numBytes;
+}
+
+}
+}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
new file mode 100644
index 00000000..55254013
--- /dev/null
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -0,0 +1,193 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Routines used by both the compiler and the expression compiler.
+ */
+
+#pragma once
+
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev {
+namespace solidity {
+
+class Type; // forward
+
+class CompilerUtils
+{
+public:
+ CompilerUtils(CompilerContext& _context): m_context(_context) {}
+
+ /// Stores the initial value of the free-memory-pointer at its position;
+ void initialiseFreeMemoryPointer();
+ /// Copies the free memory pointer to the stack.
+ void fetchFreeMemoryPointer();
+ /// Stores the free memory pointer from the stack.
+ void storeFreeMemoryPointer();
+ /// Allocates a number of bytes in memory as given on the stack.
+ /// Stack pre: <size>
+ /// Stack post: <mem_start>
+ void allocateMemory();
+ /// Appends code that transforms memptr to (memptr - free_memptr) memptr
+ void toSizeAfterFreeMemoryPointer();
+
+ /// Loads data from memory to the stack.
+ /// @param _offset offset in memory (or calldata)
+ /// @param _type data type to load
+ /// @param _fromCalldata if true, load from calldata, not from memory
+ /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries
+ /// @returns the number of bytes consumed in memory.
+ unsigned loadFromMemory(
+ unsigned _offset,
+ Type const& _type = IntegerType(256),
+ bool _fromCalldata = false,
+ bool _padToWordBoundaries = false
+ );
+ /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack.
+ /// Stack pre: memory_offset
+ /// Stack post: value... (memory_offset+length)
+ void loadFromMemoryDynamic(
+ Type const& _type,
+ bool _fromCalldata = false,
+ bool _padToWordBoundaries = true,
+ bool _keepUpdatedMemoryOffset = true
+ );
+ /// Stores a 256 bit integer from stack in memory.
+ /// @param _offset offset in memory
+ /// @param _type type of the data on the stack
+ void storeInMemory(unsigned _offset);
+ /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
+ /// and also updates that. For reference types, only copies the data pointer. Fails for
+ /// non-memory-references.
+ /// @param _padToWordBoundaries if true, adds zeros to pad to multiple of 32 bytes. Array elements
+ /// are always padded (except for byte arrays), regardless of this parameter.
+ /// Stack pre: memory_offset value...
+ /// Stack post: (memory_offset+length)
+ void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
+
+ /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given
+ /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes.
+ /// Removes the values from the stack and leaves the updated memory pointer.
+ /// Stack pre: <v1> <v2> ... <vn> <memptr>
+ /// Stack post: <memptr_updated>
+ /// Does not touch the memory-free pointer.
+ /// @param _padToWordBoundaries if false, all values are concatenated without padding.
+ /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
+ /// together with fixed-length data.
+ /// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not
+ /// convert storage pointer types to memory types.
+ /// @note the locations of target reference types are ignored, because it will always be
+ /// memory.
+ void encodeToMemory(
+ TypePointers const& _givenTypes = {},
+ TypePointers const& _targetTypes = {},
+ bool _padToWordBoundaries = true,
+ bool _copyDynamicDataInPlace = false,
+ bool _encodeAsLibraryTypes = false
+ );
+
+ /// Zero-initialises (the data part of) an already allocated memory array.
+ /// Stack pre: <length> <memptr>
+ /// Stack post: <updated_memptr>
+ void zeroInitialiseMemoryArray(ArrayType const& _type);
+
+ /// Uses a CALL to the identity contract to perform a memory-to-memory copy.
+ /// Stack pre: <size> <target> <source>
+ /// Stack post:
+ void memoryCopy();
+
+ /// Appends code for an implicit or explicit type conversion. This includes erasing higher
+ /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
+ /// if a reference type is converted from calldata or storage to memory.
+ /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
+ /// necessary.
+ void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
+
+ /// Creates a zero-value for the given type and puts it onto the stack. This might allocate
+ /// memory for memory references.
+ void pushZeroValue(Type const& _type);
+
+ /// Moves the value that is at the top of the stack to a stack variable.
+ void moveToStackVariable(VariableDeclaration const& _variable);
+ /// Copies an item that occupies @a _itemSize stack slots from a stack depth of @a _stackDepth
+ /// to the top of the stack.
+ void copyToStackTop(unsigned _stackDepth, unsigned _itemSize);
+ /// Moves an item that occupies @a _itemSize stack slots and has items occupying @a _stackDepth
+ /// slots above it to the top of the stack.
+ void moveToStackTop(unsigned _stackDepth, unsigned _itemSize = 1);
+ /// Moves @a _itemSize elements past @a _stackDepth other stack elements
+ void moveIntoStack(unsigned _stackDepth, unsigned _itemSize = 1);
+ /// Rotates the topmost @a _items items on the stack, such that the previously topmost element
+ /// is bottom-most.
+ void rotateStackUp(unsigned _items);
+ /// Rotates the topmost @a _items items on the stack, such that the previously bottom-most element
+ /// is now topmost.
+ void rotateStackDown(unsigned _items);
+ /// Removes the current value from the top of the stack.
+ void popStackElement(Type const& _type);
+ /// Removes element from the top of the stack _amount times.
+ void popStackSlots(size_t _amount);
+
+ template <class T>
+ static unsigned sizeOnStack(std::vector<T> const& _variables);
+ static unsigned sizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
+
+ /// Appends code that computes tha SHA3 hash of the topmost stack element of 32 byte type.
+ void computeHashStatic();
+
+ /// Bytes we need to the start of call data.
+ /// - The size in bytes of the function (hash) identifier.
+ static const unsigned dataStartOffset;
+
+ /// Position of the free-memory-pointer in memory;
+ static const size_t freeMemoryPointer;
+
+private:
+ /// Address of the precompiled identity contract.
+ static const unsigned identityContractAddress;
+
+ /// Stores the given string in memory.
+ /// Stack pre: mempos
+ /// Stack post:
+ void storeStringData(bytesConstRef _data);
+
+ /// Appends code that cleans higher-order bits for integer types.
+ void cleanHigherOrderBits(IntegerType const& _typeOnStack);
+
+ /// Prepares the given type for storing in memory by shifting it if necessary.
+ unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const;
+ /// Loads type from memory assuming memory offset is on stack top.
+ unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries);
+
+ CompilerContext& m_context;
+};
+
+
+template <class T>
+unsigned CompilerUtils::sizeOnStack(std::vector<T> const& _variables)
+{
+ unsigned size = 0;
+ for (T const& variable: _variables)
+ size += variable->annotation().type->sizeOnStack();
+ return size;
+}
+
+}
+}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
new file mode 100644
index 00000000..64eb6710
--- /dev/null
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -0,0 +1,1551 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity AST to EVM bytecode compiler for expressions.
+ */
+
+#include <utility>
+#include <numeric>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libdevcore/Common.h>
+#include <libdevcore/SHA3.h>
+#include <libethcore/ChainOperationParams.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/ExpressionCompiler.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+#include <libsolidity/codegen/LValue.h>
+using namespace std;
+
+// TODO: FIXME: HOMESTEAD: XXX: @chriseth Params deprecated - use EVMSchedule instead.
+
+namespace dev
+{
+namespace solidity
+{
+
+void ExpressionCompiler::compile(Expression const& _expression)
+{
+ _expression.accept(*this);
+}
+
+void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration const& _varDecl)
+{
+ if (!_varDecl.value())
+ return;
+ TypePointer type = _varDecl.value()->annotation().type;
+ solAssert(!!type, "Type information not available.");
+ CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
+ _varDecl.value()->accept(*this);
+
+ if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage))
+ {
+ // reference type, only convert value to mobile type and do final conversion in storeValue.
+ utils().convertType(*type, *type->mobileType());
+ type = type->mobileType();
+ }
+ else
+ {
+ utils().convertType(*type, *_varDecl.annotation().type);
+ type = _varDecl.annotation().type;
+ }
+ StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true);
+}
+
+void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl)
+{
+ solAssert(_varDecl.isConstant(), "");
+ _varDecl.value()->accept(*this);
+ utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type);
+
+ // append return
+ m_context << eth::dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
+ m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
+}
+
+void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
+{
+ solAssert(!_varDecl.isConstant(), "");
+ CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
+ FunctionType accessorType(_varDecl);
+
+ TypePointers paramTypes = accessorType.parameterTypes();
+
+ // retrieve the position of the variable
+ auto const& location = m_context.storageLocationOfVariable(_varDecl);
+ m_context << location.first << u256(location.second);
+
+ TypePointer returnType = _varDecl.annotation().type;
+
+ for (size_t i = 0; i < paramTypes.size(); ++i)
+ {
+ if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
+ {
+ solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
+ solAssert(
+ !paramTypes[i]->isDynamicallySized(),
+ "Accessors for mapping with dynamically-sized keys not yet implemented."
+ );
+ // pop offset
+ m_context << eth::Instruction::POP;
+ // move storage offset to memory.
+ utils().storeInMemory(32);
+ // move key to memory.
+ utils().copyToStackTop(paramTypes.size() - i, 1);
+ utils().storeInMemory(0);
+ m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
+ // push offset
+ m_context << u256(0);
+ returnType = mappingType->valueType();
+ }
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
+ {
+ // pop offset
+ m_context << eth::Instruction::POP;
+ utils().copyToStackTop(paramTypes.size() - i + 1, 1);
+ ArrayUtils(m_context).accessIndex(*arrayType);
+ returnType = arrayType->baseType();
+ }
+ else
+ solAssert(false, "Index access is allowed only for \"mapping\" and \"array\" types.");
+ }
+ // remove index arguments.
+ if (paramTypes.size() == 1)
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::POP << eth::Instruction::SWAP1;
+ else if (paramTypes.size() >= 2)
+ {
+ m_context << eth::swapInstruction(paramTypes.size());
+ m_context << eth::Instruction::POP;
+ m_context << eth::swapInstruction(paramTypes.size());
+ utils().popStackSlots(paramTypes.size() - 1);
+ }
+ unsigned retSizeOnStack = 0;
+ solAssert(accessorType.returnParameterTypes().size() >= 1, "");
+ auto const& returnTypes = accessorType.returnParameterTypes();
+ if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
+ {
+ // remove offset
+ m_context << eth::Instruction::POP;
+ auto const& names = accessorType.returnParameterNames();
+ // struct
+ for (size_t i = 0; i < names.size(); ++i)
+ {
+ if (returnTypes[i]->category() == Type::Category::Mapping)
+ continue;
+ if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i].get()))
+ if (!arrayType->isByteArray())
+ continue;
+ pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
+ m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
+ TypePointer memberType = structType->memberType(names[i]);
+ StorageItem(m_context, *memberType).retrieveValue(SourceLocation(), true);
+ utils().convertType(*memberType, *returnTypes[i]);
+ utils().moveToStackTop(returnTypes[i]->sizeOnStack());
+ retSizeOnStack += returnTypes[i]->sizeOnStack();
+ }
+ // remove slot
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ // simple value or array
+ solAssert(returnTypes.size() == 1, "");
+ StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
+ utils().convertType(*returnType, *returnTypes.front());
+ retSizeOnStack = returnTypes.front()->sizeOnStack();
+ }
+ solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
+ solAssert(retSizeOnStack <= 15, "Stack is too deep.");
+ m_context << eth::dupInstruction(retSizeOnStack + 1);
+ m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
+}
+
+bool ExpressionCompiler::visit(Conditional const& _condition)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _condition);
+ _condition.condition().accept(*this);
+ eth::AssemblyItem trueTag = m_context.appendConditionalJump();
+ _condition.falseExpression().accept(*this);
+ utils().convertType(*_condition.falseExpression().annotation().type, *_condition.annotation().type);
+ eth::AssemblyItem endTag = m_context.appendJumpToNew();
+ m_context << trueTag;
+ int offset = _condition.annotation().type->sizeOnStack();
+ m_context.adjustStackOffset(-offset);
+ _condition.trueExpression().accept(*this);
+ utils().convertType(*_condition.trueExpression().annotation().type, *_condition.annotation().type);
+ m_context << endTag;
+ return false;
+}
+
+bool ExpressionCompiler::visit(Assignment const& _assignment)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _assignment);
+ _assignment.rightHandSide().accept(*this);
+ // Perform some conversion already. This will convert storage types to memory and literals
+ // to their actual type, but will not convert e.g. memory to storage.
+ TypePointer type = _assignment.rightHandSide().annotation().type->closestTemporaryType(
+ _assignment.leftHandSide().annotation().type
+ );
+ utils().convertType(*_assignment.rightHandSide().annotation().type, *type);
+
+ _assignment.leftHandSide().accept(*this);
+ solAssert(!!m_currentLValue, "LValue not retrieved.");
+
+ Token::Value op = _assignment.assignmentOperator();
+ if (op != Token::Assign) // compound assignment
+ {
+ solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
+ unsigned lvalueSize = m_currentLValue->sizeOnStack();
+ unsigned itemSize = _assignment.annotation().type->sizeOnStack();
+ if (lvalueSize > 0)
+ {
+ utils().copyToStackTop(lvalueSize + itemSize, itemSize);
+ utils().copyToStackTop(itemSize + lvalueSize, lvalueSize);
+ // value lvalue_ref value lvalue_ref
+ }
+ m_currentLValue->retrieveValue(_assignment.location(), true);
+ appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type);
+ if (lvalueSize > 0)
+ {
+ solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
+ // value [lvalue_ref] updated_value
+ for (unsigned i = 0; i < itemSize; ++i)
+ m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
+ }
+ }
+ m_currentLValue->storeValue(*type, _assignment.location());
+ m_currentLValue.reset();
+ return false;
+}
+
+bool ExpressionCompiler::visit(TupleExpression const& _tuple)
+{
+ if (_tuple.isInlineArray())
+ {
+ ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
+
+ solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
+ m_context << max(u256(32u), arrayType.memorySize());
+ utils().allocateMemory();
+ m_context << eth::Instruction::DUP1;
+
+ for (auto const& component: _tuple.components())
+ {
+ component->accept(*this);
+ utils().convertType(*component->annotation().type, *arrayType.baseType(), true);
+ utils().storeInMemoryDynamic(*arrayType.baseType(), true);
+ }
+
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ vector<unique_ptr<LValue>> lvalues;
+ for (auto const& component: _tuple.components())
+ if (component)
+ {
+ component->accept(*this);
+ if (_tuple.annotation().lValueRequested)
+ {
+ solAssert(!!m_currentLValue, "");
+ lvalues.push_back(move(m_currentLValue));
+ }
+ }
+ else if (_tuple.annotation().lValueRequested)
+ lvalues.push_back(unique_ptr<LValue>());
+ if (_tuple.annotation().lValueRequested)
+ {
+ if (_tuple.components().size() == 1)
+ m_currentLValue = move(lvalues[0]);
+ else
+ m_currentLValue.reset(new TupleObject(m_context, move(lvalues)));
+ }
+ }
+ return false;
+}
+
+bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
+ //@todo type checking and creating code for an operator should be in the same place:
+ // the operator should know how to convert itself and to which types it applies, so
+ // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
+ // represents the operator
+ if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant)
+ {
+ m_context << _unaryOperation.annotation().type->literalValue(nullptr);
+ return false;
+ }
+
+ _unaryOperation.subExpression().accept(*this);
+
+ switch (_unaryOperation.getOperator())
+ {
+ case Token::Not: // !
+ m_context << eth::Instruction::ISZERO;
+ break;
+ case Token::BitNot: // ~
+ m_context << eth::Instruction::NOT;
+ break;
+ case Token::After: // after
+ m_context << eth::Instruction::TIMESTAMP << eth::Instruction::ADD;
+ break;
+ case Token::Delete: // delete
+ solAssert(!!m_currentLValue, "LValue not retrieved.");
+ m_currentLValue->setToZero(_unaryOperation.location());
+ m_currentLValue.reset();
+ break;
+ case Token::Inc: // ++ (pre- or postfix)
+ case Token::Dec: // -- (pre- or postfix)
+ solAssert(!!m_currentLValue, "LValue not retrieved.");
+ m_currentLValue->retrieveValue(_unaryOperation.location());
+ if (!_unaryOperation.isPrefixOperation())
+ {
+ // store value for later
+ solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
+ m_context << eth::Instruction::DUP1;
+ if (m_currentLValue->sizeOnStack() > 0)
+ for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
+ m_context << eth::swapInstruction(i);
+ }
+ m_context << u256(1);
+ if (_unaryOperation.getOperator() == Token::Inc)
+ m_context << eth::Instruction::ADD;
+ else
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ // Stack for prefix: [ref...] (*ref)+-1
+ // Stack for postfix: *ref [ref...] (*ref)+-1
+ for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
+ m_context << eth::swapInstruction(i);
+ m_currentLValue->storeValue(
+ *_unaryOperation.annotation().type, _unaryOperation.location(),
+ !_unaryOperation.isPrefixOperation());
+ m_currentLValue.reset();
+ break;
+ case Token::Add: // +
+ // unary add, so basically no-op
+ break;
+ case Token::Sub: // -
+ m_context << u256(0) << eth::Instruction::SUB;
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " +
+ string(Token::toString(_unaryOperation.getOperator()))));
+ }
+ return false;
+}
+
+bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
+ Expression const& leftExpression = _binaryOperation.leftExpression();
+ Expression const& rightExpression = _binaryOperation.rightExpression();
+ solAssert(!!_binaryOperation.annotation().commonType, "");
+ Type const& commonType = *_binaryOperation.annotation().commonType;
+ Token::Value const c_op = _binaryOperation.getOperator();
+
+ if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
+ appendAndOrOperatorCode(_binaryOperation);
+ else if (commonType.category() == Type::Category::IntegerConstant)
+ m_context << commonType.literalValue(nullptr);
+ else
+ {
+ bool cleanupNeeded = commonType.category() == Type::Category::Integer &&
+ (Token::isCompareOp(c_op) || c_op == Token::Div || c_op == Token::Mod);
+
+ // for commutative operators, push the literal as late as possible to allow improved optimization
+ auto isLiteral = [](Expression const& _e)
+ {
+ return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant;
+ };
+ bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
+ if (swap)
+ {
+ leftExpression.accept(*this);
+ utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ rightExpression.accept(*this);
+ utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ }
+ else
+ {
+ rightExpression.accept(*this);
+ utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ leftExpression.accept(*this);
+ utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ }
+ if (Token::isCompareOp(c_op))
+ appendCompareOperatorCode(c_op, commonType);
+ else
+ appendOrdinaryBinaryOperatorCode(c_op, commonType);
+ }
+
+ // do not visit the child nodes, we already did that explicitly
+ return false;
+}
+
+bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
+ using Location = FunctionType::Location;
+ if (_functionCall.annotation().isTypeConversion)
+ {
+ solAssert(_functionCall.arguments().size() == 1, "");
+ solAssert(_functionCall.names().empty(), "");
+ Expression const& firstArgument = *_functionCall.arguments().front();
+ firstArgument.accept(*this);
+ utils().convertType(*firstArgument.annotation().type, *_functionCall.annotation().type);
+ return false;
+ }
+
+ FunctionTypePointer functionType;
+ if (_functionCall.annotation().isStructConstructorCall)
+ {
+ auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
+ auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
+ functionType = structType.constructorType();
+ }
+ else
+ functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
+
+ TypePointers parameterTypes = functionType->parameterTypes();
+ vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
+ vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
+ if (!functionType->takesArbitraryParameters())
+ solAssert(callArguments.size() == parameterTypes.size(), "");
+
+ vector<ASTPointer<Expression const>> arguments;
+ if (callArgumentNames.empty())
+ // normal arguments
+ arguments = callArguments;
+ else
+ // named arguments
+ for (auto const& parameterName: functionType->parameterNames())
+ {
+ bool found = false;
+ for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
+ if ((found = (parameterName == *callArgumentNames[j])))
+ // we found the actual parameter position
+ arguments.push_back(callArguments[j]);
+ solAssert(found, "");
+ }
+
+ if (_functionCall.annotation().isStructConstructorCall)
+ {
+ TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
+ auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
+
+ m_context << max(u256(32u), structType.memorySize());
+ utils().allocateMemory();
+ m_context << eth::Instruction::DUP1;
+
+ for (unsigned i = 0; i < arguments.size(); ++i)
+ {
+ arguments[i]->accept(*this);
+ utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]);
+ utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]);
+ }
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ FunctionType const& function = *functionType;
+ if (function.bound())
+ // Only delegatecall functions can be bound, this might be lifted later.
+ solAssert(function.location() == Location::DelegateCall, "");
+ switch (function.location())
+ {
+ case Location::Internal:
+ {
+ // Calling convention: Caller pushes return address and arguments
+ // Callee removes them and pushes return values
+
+ eth::AssemblyItem returnLabel = m_context.pushNewTag();
+ for (unsigned i = 0; i < arguments.size(); ++i)
+ {
+ arguments[i]->accept(*this);
+ utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
+ }
+ _functionCall.expression().accept(*this);
+
+ m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
+ m_context << returnLabel;
+
+ unsigned returnParametersSize = CompilerUtils::sizeOnStack(function.returnParameterTypes());
+ // callee adds return parameters, but removes arguments and return label
+ m_context.adjustStackOffset(returnParametersSize - CompilerUtils::sizeOnStack(function.parameterTypes()) - 1);
+ break;
+ }
+ case Location::External:
+ case Location::CallCode:
+ case Location::DelegateCall:
+ case Location::Bare:
+ case Location::BareCallCode:
+ case Location::BareDelegateCall:
+ _functionCall.expression().accept(*this);
+ appendExternalFunctionCall(function, arguments);
+ break;
+ case Location::Creation:
+ {
+ _functionCall.expression().accept(*this);
+ solAssert(!function.gasSet(), "Gas limit set for contract creation.");
+ solAssert(function.returnParameterTypes().size() == 1, "");
+ TypePointers argumentTypes;
+ for (auto const& arg: arguments)
+ {
+ arg->accept(*this);
+ argumentTypes.push_back(arg->annotation().type);
+ }
+ ContractDefinition const& contract =
+ dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
+ // copy the contract's code into memory
+ eth::Assembly const& assembly = m_context.compiledContract(contract);
+ utils().fetchFreeMemoryPointer();
+ // pushes size
+ eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
+ m_context << eth::Instruction::DUP1 << subroutine;
+ m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
+
+ m_context << eth::Instruction::ADD;
+ utils().encodeToMemory(argumentTypes, function.parameterTypes());
+ // now on stack: memory_end_ptr
+ // need: size, offset, endowment
+ utils().toSizeAfterFreeMemoryPointer();
+ if (function.valueSet())
+ m_context << eth::dupInstruction(3);
+ else
+ m_context << u256(0);
+ m_context << eth::Instruction::CREATE;
+ if (function.valueSet())
+ m_context << eth::swapInstruction(1) << eth::Instruction::POP;
+ break;
+ }
+ case Location::SetGas:
+ {
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.expression().accept(*this);
+
+ arguments.front()->accept(*this);
+ utils().convertType(*arguments.front()->annotation().type, IntegerType(256), true);
+ // Note that function is not the original function, but the ".gas" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
+ if (stackDepth > 0)
+ m_context << eth::swapInstruction(stackDepth);
+ if (function.gasSet())
+ m_context << eth::Instruction::POP;
+ break;
+ }
+ case Location::SetValue:
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.expression().accept(*this);
+ // Note that function is not the original function, but the ".value" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ if (function.valueSet())
+ m_context << eth::Instruction::POP;
+ arguments.front()->accept(*this);
+ break;
+ case Location::Send:
+ _functionCall.expression().accept(*this);
+ m_context << u256(0); // do not send gas (there still is the stipend)
+ arguments.front()->accept(*this);
+ utils().convertType(
+ *arguments.front()->annotation().type,
+ *function.parameterTypes().front(), true
+ );
+ appendExternalFunctionCall(
+ FunctionType(
+ TypePointers{},
+ TypePointers{},
+ strings(),
+ strings(),
+ Location::Bare,
+ false,
+ nullptr,
+ true,
+ true
+ ),
+ {}
+ );
+ break;
+ case Location::Selfdestruct:
+ arguments.front()->accept(*this);
+ utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
+ m_context << eth::Instruction::SUICIDE;
+ break;
+ case Location::SHA3:
+ {
+ TypePointers argumentTypes;
+ for (auto const& arg: arguments)
+ {
+ arg->accept(*this);
+ argumentTypes.push_back(arg->annotation().type);
+ }
+ utils().fetchFreeMemoryPointer();
+ utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
+ utils().toSizeAfterFreeMemoryPointer();
+ m_context << eth::Instruction::SHA3;
+ break;
+ }
+ case Location::Log0:
+ case Location::Log1:
+ case Location::Log2:
+ case Location::Log3:
+ case Location::Log4:
+ {
+ unsigned logNumber = int(function.location()) - int(Location::Log0);
+ for (unsigned arg = logNumber; arg > 0; --arg)
+ {
+ arguments[arg]->accept(*this);
+ utils().convertType(*arguments[arg]->annotation().type, *function.parameterTypes()[arg], true);
+ }
+ arguments.front()->accept(*this);
+ utils().fetchFreeMemoryPointer();
+ utils().encodeToMemory(
+ {arguments.front()->annotation().type},
+ {function.parameterTypes().front()},
+ false,
+ true);
+ utils().toSizeAfterFreeMemoryPointer();
+ m_context << eth::logInstruction(logNumber);
+ break;
+ }
+ case Location::Event:
+ {
+ _functionCall.expression().accept(*this);
+ auto const& event = dynamic_cast<EventDefinition const&>(function.declaration());
+ unsigned numIndexed = 0;
+ // All indexed arguments go to the stack
+ for (unsigned arg = arguments.size(); arg > 0; --arg)
+ if (event.parameters()[arg - 1]->isIndexed())
+ {
+ ++numIndexed;
+ arguments[arg - 1]->accept(*this);
+ if (auto const& arrayType = dynamic_pointer_cast<ArrayType const>(function.parameterTypes()[arg - 1]))
+ {
+ utils().fetchFreeMemoryPointer();
+ utils().encodeToMemory(
+ {arguments[arg - 1]->annotation().type},
+ {arrayType},
+ false,
+ true
+ );
+ utils().toSizeAfterFreeMemoryPointer();
+ m_context << eth::Instruction::SHA3;
+ }
+ else
+ utils().convertType(
+ *arguments[arg - 1]->annotation().type,
+ *function.parameterTypes()[arg - 1],
+ true
+ );
+ }
+ if (!event.isAnonymous())
+ {
+ m_context << u256(h256::Arith(dev::sha3(function.externalSignature())));
+ ++numIndexed;
+ }
+ solAssert(numIndexed <= 4, "Too many indexed arguments.");
+ // Copy all non-indexed arguments to memory (data)
+ // Memory position is only a hack and should be removed once we have free memory pointer.
+ TypePointers nonIndexedArgTypes;
+ TypePointers nonIndexedParamTypes;
+ for (unsigned arg = 0; arg < arguments.size(); ++arg)
+ if (!event.parameters()[arg]->isIndexed())
+ {
+ arguments[arg]->accept(*this);
+ nonIndexedArgTypes.push_back(arguments[arg]->annotation().type);
+ nonIndexedParamTypes.push_back(function.parameterTypes()[arg]);
+ }
+ utils().fetchFreeMemoryPointer();
+ utils().encodeToMemory(nonIndexedArgTypes, nonIndexedParamTypes);
+ // need: topic1 ... topicn memsize memstart
+ utils().toSizeAfterFreeMemoryPointer();
+ m_context << eth::logInstruction(numIndexed);
+ break;
+ }
+ case Location::BlockHash:
+ {
+ arguments[0]->accept(*this);
+ utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
+ m_context << eth::Instruction::BLOCKHASH;
+ break;
+ }
+ case Location::AddMod:
+ case Location::MulMod:
+ {
+ for (unsigned i = 0; i < 3; i ++)
+ {
+ arguments[2 - i]->accept(*this);
+ utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256));
+ }
+ if (function.location() == Location::AddMod)
+ m_context << eth::Instruction::ADDMOD;
+ else
+ m_context << eth::Instruction::MULMOD;
+ break;
+ }
+ case Location::ECRecover:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ {
+ _functionCall.expression().accept(*this);
+ static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
+ {Location::SHA256, 2},
+ {Location::RIPEMD160, 3}};
+ m_context << contractAddresses.find(function.location())->second;
+ for (unsigned i = function.sizeOnStack(); i > 0; --i)
+ m_context << eth::swapInstruction(i);
+ appendExternalFunctionCall(function, arguments);
+ break;
+ }
+ case Location::ByteArrayPush:
+ case Location::ArrayPush:
+ {
+ _functionCall.expression().accept(*this);
+ solAssert(function.parameterTypes().size() == 1, "");
+ solAssert(!!function.parameterTypes()[0], "");
+ TypePointer paramType = function.parameterTypes()[0];
+ shared_ptr<ArrayType> arrayType =
+ function.location() == Location::ArrayPush ?
+ make_shared<ArrayType>(DataLocation::Storage, paramType) :
+ make_shared<ArrayType>(DataLocation::Storage);
+ // get the current length
+ ArrayUtils(m_context).retrieveLength(*arrayType);
+ m_context << eth::Instruction::DUP1;
+ // stack: ArrayReference currentLength currentLength
+ m_context << u256(1) << eth::Instruction::ADD;
+ // stack: ArrayReference currentLength newLength
+ m_context << eth::Instruction::DUP3 << eth::Instruction::DUP2;
+ ArrayUtils(m_context).resizeDynamicArray(*arrayType);
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
+ // stack: newLength ArrayReference oldLength
+ ArrayUtils(m_context).accessIndex(*arrayType, false);
+
+ // stack: newLength storageSlot slotOffset
+ arguments[0]->accept(*this);
+ // stack: newLength storageSlot slotOffset argValue
+ TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
+ utils().convertType(*arguments[0]->annotation().type, *type);
+ utils().moveToStackTop(1 + type->sizeOnStack());
+ utils().moveToStackTop(1 + type->sizeOnStack());
+ // stack: newLength argValue storageSlot slotOffset
+ if (function.location() == Location::ArrayPush)
+ StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
+ else
+ StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
+ break;
+ }
+ case Location::ObjectCreation:
+ {
+ // Will allocate at the end of memory (MSIZE) and not write at all unless the base
+ // type is dynamically sized.
+ ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
+ _functionCall.expression().accept(*this);
+ solAssert(arguments.size() == 1, "");
+
+ // Fetch requested length.
+ arguments[0]->accept(*this);
+ utils().convertType(*arguments[0]->annotation().type, IntegerType(256));
+
+ // Stack: requested_length
+ // Allocate at max(MSIZE, freeMemoryPointer)
+ utils().fetchFreeMemoryPointer();
+ m_context << eth::Instruction::DUP1 << eth::Instruction::MSIZE;
+ m_context << eth::Instruction::LT;
+ auto initialise = m_context.appendConditionalJump();
+ // Free memory pointer does not point to empty memory, use MSIZE.
+ m_context << eth::Instruction::POP;
+ m_context << eth::Instruction::MSIZE;
+ m_context << initialise;
+
+ // Stack: requested_length memptr
+ m_context << eth::Instruction::SWAP1;
+ // Stack: memptr requested_length
+ // store length
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::MSTORE;
+ // Stack: memptr requested_length
+ // update free memory pointer
+ m_context << eth::Instruction::DUP1 << arrayType.baseType()->memoryHeadSize();
+ m_context << eth::Instruction::MUL << u256(32) << eth::Instruction::ADD;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ utils().storeFreeMemoryPointer();
+ // Stack: memptr requested_length
+
+ // We only have to initialise if the base type is a not a value type.
+ if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
+ {
+ m_context << eth::Instruction::DUP2 << u256(32) << eth::Instruction::ADD;
+ utils().zeroInitialiseMemoryArray(arrayType);
+ m_context << eth::Instruction::POP;
+ }
+ else
+ m_context << eth::Instruction::POP;
+ break;
+ }
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
+ }
+ }
+ return false;
+}
+
+bool ExpressionCompiler::visit(NewExpression const&)
+{
+ // code is created for the function call (CREATION) only
+ return false;
+}
+
+void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
+ // Check whether the member is a bound function.
+ ASTString const& member = _memberAccess.memberName();
+ if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ if (funType->bound())
+ {
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ *funType->selfType(),
+ true
+ );
+ auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope());
+ solAssert(contract && contract->isLibrary(), "");
+ m_context.appendLibraryAddress(contract->name());
+ m_context << funType->externalIdentifier();
+ utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2);
+ return;
+ }
+
+ switch (_memberAccess.expression().annotation().type->category())
+ {
+ case Type::Category::Contract:
+ {
+ bool alsoSearchInteger = false;
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
+ if (type.isSuper())
+ {
+ solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
+ m_context << m_context.superFunctionEntryLabel(
+ dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
+ type.contractDefinition()
+ ).pushTag();
+ }
+ else
+ {
+ // ordinary contract type
+ if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
+ {
+ u256 identifier;
+ if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ identifier = FunctionType(*variable).externalIdentifier();
+ else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration))
+ identifier = FunctionType(*function).externalIdentifier();
+ else
+ solAssert(false, "Contract member is neither variable nor function.");
+ utils().convertType(type, IntegerType(0, IntegerType::Modifier::Address), true);
+ m_context << identifier;
+ }
+ else
+ // not found in contract, search in members inherited from address
+ alsoSearchInteger = true;
+ }
+ if (!alsoSearchInteger)
+ break;
+ }
+ case Type::Category::Integer:
+ if (member == "balance")
+ {
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(0, IntegerType::Modifier::Address),
+ true
+ );
+ m_context << eth::Instruction::BALANCE;
+ }
+ else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member))
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(0, IntegerType::Modifier::Address),
+ true
+ );
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
+ break;
+ case Type::Category::Function:
+ solAssert(!!_memberAccess.expression().annotation().type->memberType(member),
+ "Invalid member access to function.");
+ break;
+ case Type::Category::Magic:
+ // we can ignore the kind of magic and only look at the name of the member
+ if (member == "coinbase")
+ m_context << eth::Instruction::COINBASE;
+ else if (member == "timestamp")
+ m_context << eth::Instruction::TIMESTAMP;
+ else if (member == "difficulty")
+ m_context << eth::Instruction::DIFFICULTY;
+ else if (member == "number")
+ m_context << eth::Instruction::NUMBER;
+ else if (member == "gaslimit")
+ m_context << eth::Instruction::GASLIMIT;
+ else if (member == "sender")
+ m_context << eth::Instruction::CALLER;
+ else if (member == "value")
+ m_context << eth::Instruction::CALLVALUE;
+ else if (member == "origin")
+ m_context << eth::Instruction::ORIGIN;
+ else if (member == "gas")
+ m_context << eth::Instruction::GAS;
+ else if (member == "gasprice")
+ m_context << eth::Instruction::GASPRICE;
+ else if (member == "data")
+ m_context << u256(0) << eth::Instruction::CALLDATASIZE;
+ else if (member == "sig")
+ m_context << u256(0) << eth::Instruction::CALLDATALOAD
+ << (u256(0xffffffff) << (256 - 32)) << eth::Instruction::AND;
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
+ break;
+ case Type::Category::Struct:
+ {
+ StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type);
+ switch (type.location())
+ {
+ case DataLocation::Storage:
+ {
+ pair<u256, unsigned> const& offsets = type.storageOffsetsOfMember(member);
+ m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second);
+ setLValueToStorageItem(_memberAccess);
+ break;
+ }
+ case DataLocation::Memory:
+ {
+ m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD;
+ setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
+ break;
+ }
+ default:
+ solAssert(false, "Illegal data location for struct.");
+ }
+ break;
+ }
+ case Type::Category::Enum:
+ {
+ EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type);
+ m_context << type.memberValue(_memberAccess.memberName());
+ break;
+ }
+ case Type::Category::TypeType:
+ {
+ TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type);
+
+ if (dynamic_cast<ContractType const*>(type.actualType().get()))
+ {
+ if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ {
+ if (funType->location() != FunctionType::Location::Internal)
+ m_context << funType->externalIdentifier();
+ else
+ {
+ auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
+ solAssert(!!function, "Function not found in member access");
+ m_context << m_context.functionEntryLabel(*function).pushTag();
+ }
+ }
+ }
+ else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get()))
+ m_context << enumType->memberValue(_memberAccess.memberName());
+ break;
+ }
+ case Type::Category::Array:
+ {
+ auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
+ if (member == "length")
+ {
+ if (!type.isDynamicallySized())
+ {
+ utils().popStackElement(type);
+ m_context << type.length();
+ }
+ else
+ switch (type.location())
+ {
+ case DataLocation::CallData:
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ break;
+ case DataLocation::Storage:
+ setLValue<StorageArrayLength>(_memberAccess, type);
+ break;
+ case DataLocation::Memory:
+ m_context << eth::Instruction::MLOAD;
+ break;
+ }
+ }
+ else if (member == "push")
+ {
+ solAssert(
+ type.isDynamicallySized() && type.location() == DataLocation::Storage,
+ "Tried to use .push() on a non-dynamically sized array"
+ );
+ }
+ else
+ solAssert(false, "Illegal array member.");
+ break;
+ }
+ case Type::Category::FixedBytes:
+ {
+ auto const& type = dynamic_cast<FixedBytesType const&>(*_memberAccess.expression().annotation().type);
+ utils().popStackElement(type);
+ if (member == "length")
+ m_context << u256(type.numBytes());
+ else
+ solAssert(false, "Illegal fixed bytes member.");
+ break;
+ }
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
+ }
+}
+
+bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
+ _indexAccess.baseExpression().accept(*this);
+
+ Type const& baseType = *_indexAccess.baseExpression().annotation().type;
+
+ if (baseType.category() == Type::Category::Mapping)
+ {
+ // stack: storage_base_ref
+ TypePointer keyType = dynamic_cast<MappingType const&>(baseType).keyType();
+ solAssert(_indexAccess.indexExpression(), "Index expression expected.");
+ if (keyType->isDynamicallySized())
+ {
+ _indexAccess.indexExpression()->accept(*this);
+ utils().fetchFreeMemoryPointer();
+ // stack: base index mem
+ // note: the following operations must not allocate memory!
+ utils().encodeToMemory(
+ TypePointers{_indexAccess.indexExpression()->annotation().type},
+ TypePointers{keyType},
+ false,
+ true
+ );
+ m_context << eth::Instruction::SWAP1;
+ utils().storeInMemoryDynamic(IntegerType(256));
+ utils().toSizeAfterFreeMemoryPointer();
+ }
+ else
+ {
+ m_context << u256(0); // memory position
+ appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression());
+ m_context << eth::Instruction::SWAP1;
+ solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
+ utils().storeInMemoryDynamic(IntegerType(256));
+ m_context << u256(0);
+ }
+ m_context << eth::Instruction::SHA3;
+ m_context << u256(0);
+ setLValueToStorageItem(_indexAccess);
+ }
+ else if (baseType.category() == Type::Category::Array)
+ {
+ ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
+ solAssert(_indexAccess.indexExpression(), "Index expression expected.");
+
+ _indexAccess.indexExpression()->accept(*this);
+ // stack layout: <base_ref> [<length>] <index>
+ ArrayUtils(m_context).accessIndex(arrayType);
+ switch (arrayType.location())
+ {
+ case DataLocation::Storage:
+ if (arrayType.isByteArray())
+ {
+ solAssert(!arrayType.isString(), "Index access to string is not allowed.");
+ setLValue<StorageByteArrayElement>(_indexAccess);
+ }
+ else
+ setLValueToStorageItem(_indexAccess);
+ break;
+ case DataLocation::Memory:
+ setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
+ break;
+ case DataLocation::CallData:
+ //@todo if we implement this, the value in calldata has to be added to the base offset
+ solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
+ if (arrayType.baseType()->isValueType())
+ CompilerUtils(m_context).loadFromMemoryDynamic(
+ *arrayType.baseType(),
+ true,
+ !arrayType.isByteArray(),
+ false
+ );
+ break;
+ }
+ }
+ else if (baseType.category() == Type::Category::FixedBytes)
+ {
+ FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
+ solAssert(_indexAccess.indexExpression(), "Index expression expected.");
+
+ _indexAccess.indexExpression()->accept(*this);
+ // stack layout: <value> <index>
+ // check out-of-bounds access
+ m_context << u256(fixedBytesType.numBytes());
+ m_context << eth::Instruction::DUP2 << eth::Instruction::LT << eth::Instruction::ISZERO;
+ // out-of-bounds access throws exception
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+
+ m_context << eth::Instruction::BYTE;
+ m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL;
+ }
+ else if (baseType.category() == Type::Category::TypeType)
+ {
+ solAssert(baseType.sizeOnStack() == 0, "");
+ solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
+ // no-op - this seems to be a lone array type (`structType[];`)
+ }
+ else
+ solAssert(false, "Index access only allowed for mappings or arrays.");
+
+ return false;
+}
+
+void ExpressionCompiler::endVisit(Identifier const& _identifier)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _identifier);
+ Declaration const* declaration = _identifier.annotation().referencedDeclaration;
+ if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
+ {
+ switch (magicVar->type()->category())
+ {
+ case Type::Category::Contract:
+ // "this" or "super"
+ if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
+ m_context << eth::Instruction::ADDRESS;
+ break;
+ case Type::Category::Integer:
+ // "now"
+ m_context << eth::Instruction::TIMESTAMP;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
+ m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
+ else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ {
+ if (!variable->isConstant())
+ setLValueFromDeclaration(*declaration, _identifier);
+ else
+ {
+ variable->value()->accept(*this);
+ utils().convertType(*variable->value()->annotation().type, *variable->annotation().type);
+ }
+ }
+ else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
+ {
+ if (contract->isLibrary())
+ m_context.appendLibraryAddress(contract->name());
+ }
+ else if (dynamic_cast<EventDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else if (dynamic_cast<EnumDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else if (dynamic_cast<StructDefinition const*>(declaration))
+ {
+ // no-op
+ }
+ else
+ {
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
+ }
+}
+
+void ExpressionCompiler::endVisit(Literal const& _literal)
+{
+ CompilerContext::LocationSetter locationSetter(m_context, _literal);
+ TypePointer type = _literal.annotation().type;
+
+ switch (type->category())
+ {
+ case Type::Category::IntegerConstant:
+ case Type::Category::Bool:
+ m_context << type->literalValue(&_literal);
+ break;
+ case Type::Category::StringLiteral:
+ break; // will be done during conversion
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
+ }
+}
+
+void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
+{
+ Token::Value const c_op = _binaryOperation.getOperator();
+ solAssert(c_op == Token::Or || c_op == Token::And, "");
+
+ _binaryOperation.leftExpression().accept(*this);
+ m_context << eth::Instruction::DUP1;
+ if (c_op == Token::And)
+ m_context << eth::Instruction::ISZERO;
+ eth::AssemblyItem endLabel = m_context.appendConditionalJump();
+ m_context << eth::Instruction::POP;
+ _binaryOperation.rightExpression().accept(*this);
+ m_context << endLabel;
+}
+
+void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
+{
+ if (_operator == Token::Equal || _operator == Token::NotEqual)
+ {
+ m_context << eth::Instruction::EQ;
+ if (_operator == Token::NotEqual)
+ m_context << eth::Instruction::ISZERO;
+ }
+ else
+ {
+ bool isSigned = false;
+ if (auto type = dynamic_cast<IntegerType const*>(&_type))
+ isSigned = type->isSigned();
+
+ switch (_operator)
+ {
+ case Token::GreaterThanOrEqual:
+ m_context <<
+ (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) <<
+ eth::Instruction::ISZERO;
+ break;
+ case Token::LessThanOrEqual:
+ m_context <<
+ (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) <<
+ eth::Instruction::ISZERO;
+ break;
+ case Token::GreaterThan:
+ m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
+ break;
+ case Token::LessThan:
+ m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
+ }
+ }
+}
+
+void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
+{
+ if (Token::isArithmeticOp(_operator))
+ appendArithmeticOperatorCode(_operator, _type);
+ else if (Token::isBitOp(_operator))
+ appendBitOperatorCode(_operator);
+ else if (Token::isShiftOp(_operator))
+ appendShiftOperatorCode(_operator);
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator."));
+}
+
+void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
+{
+ IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
+ bool const c_isSigned = type.isSigned();
+
+ switch (_operator)
+ {
+ case Token::Add:
+ m_context << eth::Instruction::ADD;
+ break;
+ case Token::Sub:
+ m_context << eth::Instruction::SUB;
+ break;
+ case Token::Mul:
+ m_context << eth::Instruction::MUL;
+ break;
+ case Token::Div:
+ m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
+ break;
+ case Token::Mod:
+ m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
+ break;
+ case Token::Exp:
+ m_context << eth::Instruction::EXP;
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
+ }
+}
+
+void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
+{
+ switch (_operator)
+ {
+ case Token::BitOr:
+ m_context << eth::Instruction::OR;
+ break;
+ case Token::BitAnd:
+ m_context << eth::Instruction::AND;
+ break;
+ case Token::BitXor:
+ m_context << eth::Instruction::XOR;
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator."));
+ }
+}
+
+void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
+{
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented."));
+ switch (_operator)
+ {
+ case Token::SHL:
+ break;
+ case Token::SAR:
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
+ }
+}
+
+void ExpressionCompiler::appendExternalFunctionCall(
+ FunctionType const& _functionType,
+ vector<ASTPointer<Expression const>> const& _arguments
+)
+{
+ solAssert(
+ _functionType.takesArbitraryParameters() ||
+ _arguments.size() == _functionType.parameterTypes().size(), ""
+ );
+
+ // Assumed stack content here:
+ // <stack top>
+ // value [if _functionType.valueSet()]
+ // gas [if _functionType.gasSet()]
+ // self object [if bound - moved to top right away]
+ // function identifier [unless bare]
+ // contract address
+
+ unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0;
+ unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
+ unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
+ unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
+
+ // move self object to top
+ if (_functionType.bound())
+ utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
+
+ using FunctionKind = FunctionType::Location;
+ FunctionKind funKind = _functionType.location();
+ bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
+ bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode;
+ bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall;
+
+ unsigned retSize = 0;
+ if (returnSuccessCondition)
+ retSize = 0; // return value actually is success condition
+ else
+ for (auto const& retType: _functionType.returnParameterTypes())
+ {
+ solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call.");
+ retSize += retType->calldataEncodedSize();
+ }
+
+ // Evaluate arguments.
+ TypePointers argumentTypes;
+ TypePointers parameterTypes = _functionType.parameterTypes();
+ bool manualFunctionId =
+ (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) &&
+ !_arguments.empty() &&
+ _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
+ CompilerUtils::dataStartOffset;
+ if (manualFunctionId)
+ {
+ // If we have a Bare* and the first type has exactly 4 bytes, use it as
+ // function identifier.
+ _arguments.front()->accept(*this);
+ utils().convertType(
+ *_arguments.front()->annotation().type,
+ IntegerType(8 * CompilerUtils::dataStartOffset),
+ true
+ );
+ for (unsigned i = 0; i < gasValueSize; ++i)
+ m_context << eth::swapInstruction(gasValueSize - i);
+ gasStackPos++;
+ valueStackPos++;
+ }
+ if (_functionType.bound())
+ {
+ argumentTypes.push_back(_functionType.selfType());
+ parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
+ }
+ for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
+ {
+ _arguments[i]->accept(*this);
+ argumentTypes.push_back(_arguments[i]->annotation().type);
+ }
+
+ // Copy function identifier to memory.
+ utils().fetchFreeMemoryPointer();
+ if (!_functionType.isBareCall() || manualFunctionId)
+ {
+ m_context << eth::dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
+ utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
+ }
+ // If the function takes arbitrary parameters, copy dynamic length data in place.
+ // Move argumenst to memory, will not update the free memory pointer (but will update the memory
+ // pointer on the stack).
+ utils().encodeToMemory(
+ argumentTypes,
+ parameterTypes,
+ _functionType.padArguments(),
+ _functionType.takesArbitraryParameters(),
+ isCallCode || isDelegateCall
+ );
+
+ // Stack now:
+ // <stack top>
+ // input_memory_end
+ // value [if _functionType.valueSet()]
+ // gas [if _functionType.gasSet()]
+ // function identifier [unless bare]
+ // contract address
+
+ // Output data will replace input data.
+ // put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
+ m_context << u256(retSize);
+ utils().fetchFreeMemoryPointer();
+ m_context << eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::SUB;
+ m_context << eth::Instruction::DUP2;
+
+ // CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
+ // [value,] addr, gas (stack top)
+ if (isDelegateCall)
+ solAssert(!_functionType.valueSet(), "Value set for delegatecall");
+ else if (_functionType.valueSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
+ else
+ m_context << u256(0);
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+
+ if (_functionType.gasSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
+ else
+ {
+ eth::EVMSchedule schedule;
+ // send all gas except the amount needed to execute "SUB" and "CALL"
+ // @todo this retains too much gas for now, needs to be fine-tuned.
+ u256 gasNeededByCaller = schedule.callGas + 10;
+ if (_functionType.valueSet())
+ gasNeededByCaller += schedule.callValueTransferGas;
+ if (!isCallCode && !isDelegateCall)
+ gasNeededByCaller += schedule.callNewAccountGas; // we never know
+ m_context <<
+ gasNeededByCaller <<
+ eth::Instruction::GAS <<
+ eth::Instruction::SUB;
+ }
+ if (isDelegateCall)
+ m_context << eth::Instruction::DELEGATECALL;
+ else if (isCallCode)
+ m_context << eth::Instruction::CALLCODE;
+ else
+ m_context << eth::Instruction::CALL;
+
+ unsigned remainsSize =
+ 2 + // contract address, input_memory_end
+ _functionType.valueSet() +
+ _functionType.gasSet() +
+ (!_functionType.isBareCall() || manualFunctionId);
+
+ if (returnSuccessCondition)
+ m_context << eth::swapInstruction(remainsSize);
+ else
+ {
+ //Propagate error condition (if CALL pushes 0 on stack).
+ m_context << eth::Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
+ utils().popStackSlots(remainsSize);
+
+ if (returnSuccessCondition)
+ {
+ // already there
+ }
+ else if (funKind == FunctionKind::RIPEMD160)
+ {
+ // fix: built-in contract returns right-aligned data
+ utils().fetchFreeMemoryPointer();
+ utils().loadFromMemoryDynamic(IntegerType(160), false, true, false);
+ utils().convertType(IntegerType(160), FixedBytesType(20));
+ }
+ else if (!_functionType.returnParameterTypes().empty())
+ {
+ utils().fetchFreeMemoryPointer();
+ bool memoryNeeded = false;
+ for (auto const& retType: _functionType.returnParameterTypes())
+ {
+ utils().loadFromMemoryDynamic(*retType, false, true, true);
+ if (dynamic_cast<ReferenceType const*>(retType.get()))
+ memoryNeeded = true;
+ }
+ if (memoryNeeded)
+ utils().storeFreeMemoryPointer();
+ else
+ m_context << eth::Instruction::POP;
+ }
+}
+
+void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
+{
+ solAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
+ _expression.accept(*this);
+ utils().convertType(*_expression.annotation().type, _expectedType, true);
+ utils().storeInMemoryDynamic(_expectedType);
+}
+
+void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
+{
+ if (m_context.isLocalVariable(&_declaration))
+ setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
+ else if (m_context.isStateVariable(&_declaration))
+ setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError()
+ << errinfo_sourceLocation(_expression.location())
+ << errinfo_comment("Identifier type not supported or identifier not found."));
+}
+
+void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
+{
+ setLValue<StorageItem>(_expression, *_expression.annotation().type);
+}
+
+CompilerUtils ExpressionCompiler::utils()
+{
+ return CompilerUtils(m_context);
+}
+
+}
+}
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
new file mode 100644
index 00000000..f00b24e8
--- /dev/null
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -0,0 +1,139 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Solidity AST to EVM bytecode compiler for expressions.
+ */
+
+#include <functional>
+#include <memory>
+#include <boost/noncopyable.hpp>
+#include <libdevcore/Common.h>
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/codegen/LValue.h>
+#include <libsolidity/interface/Utils.h>
+
+namespace dev {
+namespace eth
+{
+class AssemblyItem; // forward
+}
+namespace solidity {
+
+// forward declarations
+class CompilerContext;
+class CompilerUtils;
+class Type;
+class IntegerType;
+class ArrayType;
+
+/**
+ * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
+ * of EVM instructions. It needs a compiler context that is the same for the whole compilation
+ * unit.
+ */
+class ExpressionCompiler: private ASTConstVisitor
+{
+public:
+ /// Appends code for a State Variable accessor function
+ static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
+
+ explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
+ m_optimize(_optimize), m_context(_compilerContext) {}
+
+ /// Compile the given @a _expression and leave its value on the stack.
+ void compile(Expression const& _expression);
+
+ /// Appends code to set a state variable to its initial value/expression.
+ void appendStateVariableInitialization(VariableDeclaration const& _varDecl);
+
+ /// Appends code for a State Variable accessor function
+ void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
+
+ /// Appends code for a Constant State Variable accessor function
+ void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl);
+
+private:
+ virtual bool visit(Conditional const& _condition) override;
+ virtual bool visit(Assignment const& _assignment) override;
+ virtual bool visit(TupleExpression const& _tuple) override;
+ virtual bool visit(UnaryOperation const& _unaryOperation) override;
+ virtual bool visit(BinaryOperation const& _binaryOperation) override;
+ virtual bool visit(FunctionCall const& _functionCall) override;
+ virtual bool visit(NewExpression const& _newExpression) override;
+ virtual void endVisit(MemberAccess const& _memberAccess) override;
+ virtual bool visit(IndexAccess const& _indexAccess) override;
+ virtual void endVisit(Identifier const& _identifier) override;
+ virtual void endVisit(Literal const& _literal) override;
+
+ ///@{
+ ///@name Append code for various operator types
+ void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation);
+ void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
+ void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
+
+ void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
+ void appendBitOperatorCode(Token::Value _operator);
+ void appendShiftOperatorCode(Token::Value _operator);
+ /// @}
+
+ /// Appends code to call a function of the given type with the given arguments.
+ void appendExternalFunctionCall(
+ FunctionType const& _functionType,
+ std::vector<ASTPointer<Expression const>> const& _arguments
+ );
+ /// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
+ /// expected to be on the stack and is updated by this call.
+ void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
+
+ /// Sets the current LValue to a new one (of the appropriate type) from the given declaration.
+ /// Also retrieves the value if it was not requested by @a _expression.
+ void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression);
+ /// Sets the current LValue to a StorageItem holding the type of @a _expression. The reference is assumed
+ /// to be on the stack.
+ /// Also retrieves the value if it was not requested by @a _expression.
+ void setLValueToStorageItem(Expression const& _expression);
+ /// Sets the current LValue to a new LValue constructed from the arguments.
+ /// Also retrieves the value if it was not requested by @a _expression.
+ template <class _LValueType, class... _Arguments>
+ void setLValue(Expression const& _expression, _Arguments const&... _arguments);
+
+ /// @returns the CompilerUtils object containing the current context.
+ CompilerUtils utils();
+
+ bool m_optimize;
+ CompilerContext& m_context;
+ std::unique_ptr<LValue> m_currentLValue;
+
+};
+
+template <class _LValueType, class... _Arguments>
+void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments const&... _arguments)
+{
+ solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one.");
+ std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...));
+ if (_expression.annotation().lValueRequested)
+ m_currentLValue = move(lvalue);
+ else
+ lvalue->retrieveValue(_expression.location(), true);
+}
+
+}
+}
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
new file mode 100644
index 00000000..fdef6937
--- /dev/null
+++ b/libsolidity/codegen/LValue.cpp
@@ -0,0 +1,556 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * LValues for use in the expresison compiler.
+ */
+
+#include <libsolidity/codegen/LValue.h>
+#include <libevmcore/Instruction.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+
+using namespace std;
+using namespace dev;
+using namespace solidity;
+
+
+StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
+ LValue(_compilerContext, _declaration.annotation().type.get()),
+ m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)),
+ m_size(m_dataType->sizeOnStack())
+{
+}
+
+void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
+{
+ unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
+ if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
+ for (unsigned i = 0; i < m_size; ++i)
+ m_context << eth::dupInstruction(stackPos + 1);
+}
+
+void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
+{
+ unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
+ if (stackDiff > 16)
+ BOOST_THROW_EXCEPTION(
+ CompilerError() <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment("Stack too deep, try removing local variables.")
+ );
+ else if (stackDiff > 0)
+ for (unsigned i = 0; i < m_size; ++i)
+ m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
+ if (!_move)
+ retrieveValue(_location);
+}
+
+void StackVariable::setToZero(SourceLocation const& _location, bool) const
+{
+ CompilerUtils(m_context).pushZeroValue(*m_dataType);
+ storeValue(*m_dataType, _location, true);
+}
+
+MemoryItem::MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded):
+ LValue(_compilerContext, &_type),
+ m_padded(_padded)
+{
+}
+
+void MemoryItem::retrieveValue(SourceLocation const&, bool _remove) const
+{
+ if (m_dataType->isValueType())
+ {
+ if (!_remove)
+ m_context << eth::Instruction::DUP1;
+ CompilerUtils(m_context).loadFromMemoryDynamic(*m_dataType, false, m_padded, false);
+ }
+ else
+ m_context << eth::Instruction::MLOAD;
+}
+
+void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const
+{
+ CompilerUtils utils(m_context);
+ if (m_dataType->isValueType())
+ {
+ solAssert(_sourceType.isValueType(), "");
+ utils.moveIntoStack(_sourceType.sizeOnStack());
+ utils.convertType(_sourceType, *m_dataType, true);
+ if (!_move)
+ {
+ utils.moveToStackTop(m_dataType->sizeOnStack());
+ utils.copyToStackTop(1 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack());
+ }
+ if (!m_padded)
+ {
+ solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type.");
+ if (m_dataType->category() == Type::Category::FixedBytes)
+ m_context << u256(0) << eth::Instruction::BYTE;
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::MSTORE8;
+ }
+ else
+ {
+ utils.storeInMemoryDynamic(*m_dataType, m_padded);
+ m_context << eth::Instruction::POP;
+ }
+ }
+ else
+ {
+ solAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory.");
+
+ solAssert(m_dataType->sizeOnStack() == 1, "");
+ if (!_move)
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
+ // stack: [value] value lvalue
+ // only store the reference
+ m_context << eth::Instruction::MSTORE;
+ }
+}
+
+void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const
+{
+ CompilerUtils utils(m_context);
+ if (!_removeReference)
+ m_context << eth::Instruction::DUP1;
+ utils.pushZeroValue(*m_dataType);
+ utils.storeInMemoryDynamic(*m_dataType, m_padded);
+ m_context << eth::Instruction::POP;
+}
+
+StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
+ StorageItem(_compilerContext, *_declaration.annotation().type)
+{
+ auto const& location = m_context.storageLocationOfVariable(_declaration);
+ m_context << location.first << u256(location.second);
+}
+
+StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
+ LValue(_compilerContext, &_type)
+{
+ if (m_dataType->isValueType())
+ {
+ solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
+ solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
+ }
+}
+
+void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
+{
+ // stack: storage_key storage_offset
+ if (!m_dataType->isValueType())
+ {
+ solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size.");
+ if (_remove)
+ m_context << eth::Instruction::POP; // remove byte offset
+ else
+ m_context << eth::Instruction::DUP2;
+ return;
+ }
+ if (!_remove)
+ CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
+ if (m_dataType->storageBytes() == 32)
+ m_context << eth::Instruction::POP << eth::Instruction::SLOAD;
+ else
+ {
+ m_context
+ << eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1
+ << u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ if (m_dataType->category() == Type::Category::FixedBytes)
+ m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << eth::Instruction::MUL;
+ else if (
+ m_dataType->category() == Type::Category::Integer &&
+ dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
+ )
+ m_context << u256(m_dataType->storageBytes() - 1) << eth::Instruction::SIGNEXTEND;
+ else
+ m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << eth::Instruction::AND;
+ }
+}
+
+void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
+{
+ CompilerUtils utils(m_context);
+ // stack: value storage_key storage_offset
+ if (m_dataType->isValueType())
+ {
+ solAssert(m_dataType->storageBytes() <= 32, "Invalid storage bytes size.");
+ solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
+ if (m_dataType->storageBytes() == 32)
+ {
+ // offset should be zero
+ m_context << eth::Instruction::POP;
+ if (!_move)
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
+ m_context << eth::Instruction::SSTORE;
+ }
+ else
+ {
+ // OR the value into the other values in the storage slot
+ m_context << u256(0x100) << eth::Instruction::EXP;
+ // stack: value storage_ref multiplier
+ // fetch old value
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ // stack: value storege_ref multiplier old_full_value
+ // clear bytes in old value
+ m_context
+ << eth::Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
+ << eth::Instruction::MUL;
+ m_context << eth::Instruction::NOT << eth::Instruction::AND;
+ // stack: value storage_ref multiplier cleared_value
+ m_context
+ << eth::Instruction::SWAP1 << eth::Instruction::DUP4;
+ // stack: value storage_ref cleared_value multiplier value
+ if (m_dataType->category() == Type::Category::FixedBytes)
+ m_context
+ << (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
+ << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ else if (
+ m_dataType->category() == Type::Category::Integer &&
+ dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
+ )
+ // remove the higher order bits
+ m_context
+ << (u256(1) << (8 * (32 - m_dataType->storageBytes())))
+ << eth::Instruction::SWAP1
+ << eth::Instruction::DUP2
+ << eth::Instruction::MUL
+ << eth::Instruction::DIV;
+ m_context << eth::Instruction::MUL << eth::Instruction::OR;
+ // stack: value storage_ref updated_value
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ if (_move)
+ m_context << eth::Instruction::POP;
+ }
+ }
+ else
+ {
+ solAssert(
+ _sourceType.category() == m_dataType->category(),
+ "Wrong type conversation for assignment.");
+ if (m_dataType->category() == Type::Category::Array)
+ {
+ m_context << eth::Instruction::POP; // remove byte offset
+ ArrayUtils(m_context).copyArrayToStorage(
+ dynamic_cast<ArrayType const&>(*m_dataType),
+ dynamic_cast<ArrayType const&>(_sourceType)
+ );
+ if (_move)
+ m_context << eth::Instruction::POP;
+ }
+ else if (m_dataType->category() == Type::Category::Struct)
+ {
+ // stack layout: source_ref target_ref target_offset
+ // note that we have structs, so offset should be zero and are ignored
+ m_context << eth::Instruction::POP;
+ auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
+ auto const& sourceType = dynamic_cast<StructType const&>(_sourceType);
+ solAssert(
+ structType.structDefinition() == sourceType.structDefinition(),
+ "Struct assignment with conversion."
+ );
+ solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
+ for (auto const& member: structType.members(nullptr))
+ {
+ // assign each member that is not a mapping
+ TypePointer const& memberType = member.type;
+ if (memberType->category() == Type::Category::Mapping)
+ continue;
+ TypePointer sourceMemberType = sourceType.memberType(member.name);
+ if (sourceType.location() == DataLocation::Storage)
+ {
+ // stack layout: source_ref target_ref
+ pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
+ m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << u256(offsets.second);
+ // stack: source_ref target_ref source_member_ref source_member_off
+ StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
+ // stack: source_ref target_ref source_value...
+ }
+ else
+ {
+ solAssert(sourceType.location() == DataLocation::Memory, "");
+ // stack layout: source_ref target_ref
+ TypePointer sourceMemberType = sourceType.memberType(member.name);
+ m_context << sourceType.memoryOffsetOfMember(member.name);
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
+ // stack layout: source_ref target_ref source_value...
+ }
+ unsigned stackSize = sourceMemberType->sizeOnStack();
+ pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
+ m_context << eth::dupInstruction(1 + stackSize) << offsets.first << eth::Instruction::ADD;
+ m_context << u256(offsets.second);
+ // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
+ StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
+ }
+ // stack layout: source_ref target_ref
+ solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size.");
+ if (_move)
+ utils.popStackSlots(2);
+ else
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+ }
+ else
+ BOOST_THROW_EXCEPTION(
+ InternalCompilerError()
+ << errinfo_sourceLocation(_location)
+ << errinfo_comment("Invalid non-value type for assignment."));
+ }
+}
+
+void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
+{
+ if (m_dataType->category() == Type::Category::Array)
+ {
+ if (!_removeReference)
+ CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
+ ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(*m_dataType));
+ }
+ else if (m_dataType->category() == Type::Category::Struct)
+ {
+ // stack layout: storage_key storage_offset
+ // @todo this can be improved: use StorageItem for non-value types, and just store 0 in
+ // all slots that contain value types later.
+ auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
+ for (auto const& member: structType.members(nullptr))
+ {
+ // zero each member that is not a mapping
+ TypePointer const& memberType = member.type;
+ if (memberType->category() == Type::Category::Mapping)
+ continue;
+ pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
+ m_context
+ << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD
+ << u256(offsets.second);
+ StorageItem(m_context, *memberType).setToZero();
+ }
+ if (_removeReference)
+ m_context << eth::Instruction::POP << eth::Instruction::POP;
+ }
+ else
+ {
+ solAssert(m_dataType->isValueType(), "Clearing of unsupported type requested: " + m_dataType->toString());
+ if (!_removeReference)
+ CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
+ if (m_dataType->storageBytes() == 32)
+ {
+ // offset should be zero
+ m_context
+ << eth::Instruction::POP << u256(0)
+ << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ }
+ else
+ {
+ m_context << u256(0x100) << eth::Instruction::EXP;
+ // stack: storage_ref multiplier
+ // fetch old value
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ // stack: storege_ref multiplier old_full_value
+ // clear bytes in old value
+ m_context
+ << eth::Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
+ << eth::Instruction::MUL;
+ m_context << eth::Instruction::NOT << eth::Instruction::AND;
+ // stack: storage_ref cleared_value
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ }
+ }
+}
+
+/// Used in StorageByteArrayElement
+static FixedBytesType byteType(1);
+
+StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext):
+ LValue(_compilerContext, &byteType)
+{
+}
+
+void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) const
+{
+ // stack: ref byte_number
+ if (_remove)
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD
+ << eth::Instruction::SWAP1 << eth::Instruction::BYTE;
+ else
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD
+ << eth::Instruction::DUP2 << eth::Instruction::BYTE;
+ m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL;
+}
+
+void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const
+{
+ // stack: value ref byte_number
+ m_context << u256(31) << eth::Instruction::SUB << u256(0x100) << eth::Instruction::EXP;
+ // stack: value ref (1<<(8*(31-byte_number)))
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ // stack: value ref (1<<(8*(31-byte_number))) old_full_value
+ // clear byte in old value
+ m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL
+ << eth::Instruction::NOT << eth::Instruction::AND;
+ // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte
+ m_context << eth::Instruction::SWAP1;
+ m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV
+ << eth::Instruction::MUL << eth::Instruction::OR;
+ // stack: value ref new_full_value
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+ if (_move)
+ m_context << eth::Instruction::POP;
+}
+
+void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const
+{
+ // stack: ref byte_number
+ if (!_removeReference)
+ m_context << eth::Instruction::DUP2 << eth::Instruction::DUP2;
+ m_context << u256(31) << eth::Instruction::SUB << u256(0x100) << eth::Instruction::EXP;
+ // stack: ref (1<<(8*(31-byte_number)))
+ m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
+ // stack: ref (1<<(8*(31-byte_number))) old_full_value
+ // clear byte in old value
+ m_context << eth::Instruction::SWAP1 << u256(0xff) << eth::Instruction::MUL;
+ m_context << eth::Instruction::NOT << eth::Instruction::AND;
+ // stack: ref old_full_value_with_cleared_byte
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
+}
+
+StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const ArrayType& _arrayType):
+ LValue(_compilerContext, _arrayType.memberType("length").get()),
+ m_arrayType(_arrayType)
+{
+ solAssert(m_arrayType.isDynamicallySized(), "");
+}
+
+void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const
+{
+ ArrayUtils(m_context).retrieveLength(m_arrayType);
+ if (_remove)
+ m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
+}
+
+void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const
+{
+ if (_move)
+ m_context << eth::Instruction::SWAP1;
+ else
+ m_context << eth::Instruction::DUP2;
+ ArrayUtils(m_context).resizeDynamicArray(m_arrayType);
+}
+
+void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const
+{
+ if (!_removeReference)
+ m_context << eth::Instruction::DUP1;
+ ArrayUtils(m_context).clearDynamicArray(m_arrayType);
+}
+
+
+TupleObject::TupleObject(
+ CompilerContext& _compilerContext,
+ std::vector<std::unique_ptr<LValue>>&& _lvalues
+):
+ LValue(_compilerContext), m_lvalues(move(_lvalues))
+{
+}
+
+unsigned TupleObject::sizeOnStack() const
+{
+ unsigned size = 0;
+ for (auto const& lv: m_lvalues)
+ if (lv)
+ size += lv->sizeOnStack();
+ return size;
+}
+
+void TupleObject::retrieveValue(SourceLocation const& _location, bool _remove) const
+{
+ unsigned initialDepth = sizeOnStack();
+ unsigned initialStack = m_context.stackHeight();
+ for (auto const& lv: m_lvalues)
+ if (lv)
+ {
+ solAssert(initialDepth + m_context.stackHeight() >= initialStack, "");
+ unsigned depth = initialDepth + m_context.stackHeight() - initialStack;
+ if (lv->sizeOnStack() > 0)
+ {
+ if (_remove && depth > lv->sizeOnStack())
+ CompilerUtils(m_context).moveToStackTop(depth, depth - lv->sizeOnStack());
+ else if (!_remove && depth > 0)
+ CompilerUtils(m_context).copyToStackTop(depth, lv->sizeOnStack());
+ }
+ lv->retrieveValue(_location, true);
+ }
+}
+
+void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _location, bool) const
+{
+ // values are below the lvalue references
+ unsigned valuePos = sizeOnStack();
+ TypePointers const& valueTypes = dynamic_cast<TupleType const&>(_sourceType).components();
+ solAssert(valueTypes.size() == m_lvalues.size(), "");
+ // valuePos .... refPos ...
+ // We will assign from right to left to optimize stack layout.
+ for (size_t i = 0; i < m_lvalues.size(); ++i)
+ {
+ unique_ptr<LValue> const& lvalue = m_lvalues[m_lvalues.size() - i - 1];
+ TypePointer const& valType = valueTypes[valueTypes.size() - i - 1];
+ unsigned stackHeight = m_context.stackHeight();
+ solAssert(!valType == !lvalue, "");
+ if (!lvalue)
+ continue;
+ valuePos += valType->sizeOnStack();
+ // copy value to top
+ CompilerUtils(m_context).copyToStackTop(valuePos, valType->sizeOnStack());
+ // move lvalue ref above value
+ CompilerUtils(m_context).moveToStackTop(valType->sizeOnStack(), lvalue->sizeOnStack());
+ lvalue->storeValue(*valType, _location, true);
+ valuePos += m_context.stackHeight() - stackHeight;
+ }
+ // As the type of an assignment to a tuple type is the empty tuple, we always move.
+ CompilerUtils(m_context).popStackElement(_sourceType);
+}
+
+void TupleObject::setToZero(SourceLocation const& _location, bool _removeReference) const
+{
+ if (_removeReference)
+ {
+ for (size_t i = 0; i < m_lvalues.size(); ++i)
+ if (m_lvalues[m_lvalues.size() - i])
+ m_lvalues[m_lvalues.size() - i]->setToZero(_location, true);
+ }
+ else
+ {
+ unsigned depth = sizeOnStack();
+ for (auto const& val: m_lvalues)
+ if (val)
+ {
+ if (val->sizeOnStack() > 0)
+ CompilerUtils(m_context).copyToStackTop(depth, val->sizeOnStack());
+ val->setToZero(_location, false);
+ depth -= val->sizeOnStack();
+ }
+ }
+}
diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h
new file mode 100644
index 00000000..e8c3aa80
--- /dev/null
+++ b/libsolidity/codegen/LValue.h
@@ -0,0 +1,224 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * LValues for use in the expresison compiler.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/codegen/ArrayUtils.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Declaration;
+class Type;
+class TupleType;
+class ArrayType;
+class CompilerContext;
+class VariableDeclaration;
+
+/**
+ * Abstract class used to retrieve, delete and store data in lvalues/variables.
+ */
+class LValue
+{
+protected:
+ explicit LValue(CompilerContext& _compilerContext, Type const* _dataType = nullptr):
+ m_context(_compilerContext), m_dataType(_dataType) {}
+
+public:
+ /// @returns the number of stack slots occupied by the lvalue reference
+ virtual unsigned sizeOnStack() const { return 1; }
+ /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
+ /// also removes the reference from the stack.
+ /// @a _location source location of the current expression, used for error reporting.
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const = 0;
+ /// Moves a value from the stack to the lvalue. Removes the value if @a _move is true.
+ /// @a _location is the source location of the expression that caused this operation.
+ /// Stack pre: value [lvalue_ref]
+ /// Stack post: if !_move: value_of(lvalue_ref)
+ virtual void storeValue(Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(), bool _move = false) const = 0;
+ /// Stores zero in the lvalue. Removes the reference from the stack if @a _removeReference is true.
+ /// @a _location is the source location of the requested operation
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const = 0;
+
+protected:
+ CompilerContext& m_context;
+ Type const* m_dataType;
+};
+
+/**
+ * Local variable that is completely stored on the stack.
+ */
+class StackVariable: public LValue
+{
+public:
+ StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
+
+ virtual unsigned sizeOnStack() const override { return 0; }
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+
+private:
+ /// Base stack offset (@see CompilerContext::baseStackOffsetOfVariable) of the local variable.
+ unsigned m_baseStackOffset;
+ /// Number of stack elements occupied by the value (not the reference).
+ unsigned m_size;
+};
+
+/**
+ * Reference to some item in memory.
+ */
+class MemoryItem: public LValue
+{
+public:
+ MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true);
+ virtual unsigned sizeOnStack() const override { return 1; }
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+private:
+ /// Special flag to deal with byte array elements.
+ bool m_padded = false;
+};
+
+/**
+ * Reference to some item in storage. On the stack this is <storage key> <offset_inside_value>,
+ * where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied
+ * by 2**i before storing it.
+ */
+class StorageItem: public LValue
+{
+public:
+ /// Constructs the LValue and pushes the location of @a _declaration onto the stack.
+ StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
+ /// Constructs the LValue and assumes that the storage reference is already on the stack.
+ StorageItem(CompilerContext& _compilerContext, Type const& _type);
+ virtual unsigned sizeOnStack() const override { return 2; }
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+};
+
+/**
+ * Reference to a single byte inside a storage byte array.
+ * Stack: <storage_ref> <byte_number>
+ */
+class StorageByteArrayElement: public LValue
+{
+public:
+ /// Constructs the LValue and assumes that the storage reference is already on the stack.
+ StorageByteArrayElement(CompilerContext& _compilerContext);
+ virtual unsigned sizeOnStack() const override { return 2; }
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+};
+
+/**
+ * Reference to the "length" member of a dynamically-sized array. This is an LValue with special
+ * semantics since assignments to it might reduce its length and thus arrays members have to be
+ * deleted.
+ */
+class StorageArrayLength: public LValue
+{
+public:
+ /// Constructs the LValue, assumes that the reference to the array head is already on the stack.
+ StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType);
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+
+private:
+ ArrayType const& m_arrayType;
+};
+
+/**
+ * Tuple object that can itself hold several LValues.
+ */
+class TupleObject: public LValue
+{
+public:
+ /// Constructs the LValue assuming that the other LValues are present on the stack.
+ /// Empty unique_ptrs are possible if e.g. some values should be ignored during assignment.
+ TupleObject(CompilerContext& _compilerContext, std::vector<std::unique_ptr<LValue>>&& _lvalues);
+ virtual unsigned sizeOnStack() const override;
+ virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
+ virtual void storeValue(
+ Type const& _sourceType,
+ SourceLocation const& _location = SourceLocation(),
+ bool _move = false
+ ) const override;
+ virtual void setToZero(
+ SourceLocation const& _location = SourceLocation(),
+ bool _removeReference = true
+ ) const override;
+
+private:
+ std::vector<std::unique_ptr<LValue>> m_lvalues;
+};
+
+}
+}
diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp
new file mode 100644
index 00000000..24fbab13
--- /dev/null
+++ b/libsolidity/formal/Why3Translator.cpp
@@ -0,0 +1,681 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Component that translates Solidity code into the why3 programming language.
+ */
+
+#include <libsolidity/formal/Why3Translator.h>
+#include <boost/algorithm/string/predicate.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+bool Why3Translator::process(SourceUnit const& _source)
+{
+ try
+ {
+ if (m_lines.size() != 1 || !m_lines.back().contents.empty())
+ fatalError(_source, "Multiple source units not yet supported");
+ appendPreface();
+ _source.accept(*this);
+ }
+ catch (FatalError& /*_e*/)
+ {
+ solAssert(m_errorOccured, "");
+ }
+ return !m_errorOccured;
+}
+
+string Why3Translator::translation() const
+{
+ string result;
+ for (auto const& line: m_lines)
+ result += string(line.indentation, '\t') + line.contents + "\n";
+ return result;
+}
+
+void Why3Translator::error(ASTNode const& _node, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::Why3TranslatorError);
+ *err <<
+ errinfo_sourceLocation(_node.location()) <<
+ errinfo_comment(_description);
+ m_errors.push_back(err);
+ m_errorOccured = true;
+}
+
+void Why3Translator::fatalError(ASTNode const& _node, string const& _description)
+{
+ error(_node, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+void Why3Translator::appendPreface()
+{
+ m_lines.push_back(Line{R"(
+module UInt256
+ use import mach.int.Unsigned
+ type uint256
+ constant max_uint256: int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ clone export mach.int.Unsigned with
+ type t = uint256,
+ constant max = max_uint256
+end
+)", 0});
+}
+
+string Why3Translator::toFormalType(Type const& _type) const
+{
+ if (auto type = dynamic_cast<IntegerType const*>(&_type))
+ {
+ if (!type->isAddress() && !type->isSigned() && type->numBits() == 256)
+ return "uint256";
+ }
+ else if (auto type = dynamic_cast<ArrayType const*>(&_type))
+ if (!type->isByteArray() && type->isDynamicallySized() && type->dataStoredIn(DataLocation::Memory))
+ {
+ string base = toFormalType(*type->baseType());
+ if (!base.empty())
+ return "array " + base;
+ }
+
+ return "";
+}
+
+void Why3Translator::addLine(string const& _line)
+{
+ newLine();
+ add(_line);
+ newLine();
+}
+
+void Why3Translator::add(string const& _str)
+{
+ m_lines.back().contents += _str;
+}
+
+void Why3Translator::newLine()
+{
+ if (!m_lines.back().contents.empty())
+ m_lines.push_back({"", m_lines.back().indentation});
+}
+
+void Why3Translator::unindent()
+{
+ newLine();
+ solAssert(m_lines.back().indentation > 0, "");
+ m_lines.back().indentation--;
+}
+
+bool Why3Translator::visit(ContractDefinition const& _contract)
+{
+ if (m_seenContract)
+ error(_contract, "More than one contract not supported.");
+ m_seenContract = true;
+ if (_contract.isLibrary())
+ error(_contract, "Libraries not supported.");
+
+ addLine("module Contract_" + _contract.name());
+ indent();
+ addLine("use import int.Int");
+ addLine("use import ref.Ref");
+ addLine("use import map.Map");
+ addLine("use import array.Array");
+ addLine("use import int.ComputerDivision");
+ addLine("use import mach.int.Unsigned");
+ addLine("use import UInt256");
+ addLine("exception Ret");
+
+ addLine("type state = {");
+ indent();
+ m_stateVariables = _contract.stateVariables();
+ for (VariableDeclaration const* variable: m_stateVariables)
+ {
+ string varType = toFormalType(*variable->annotation().type);
+ if (varType.empty())
+ fatalError(*variable, "Type not supported.");
+ addLine("mutable _" + variable->name() + ": ref " + varType);
+ }
+ unindent();
+ addLine("}");
+
+ if (!_contract.baseContracts().empty())
+ error(*_contract.baseContracts().front(), "Inheritance not supported.");
+ if (!_contract.definedStructs().empty())
+ error(*_contract.definedStructs().front(), "User-defined types not supported.");
+ if (!_contract.definedEnums().empty())
+ error(*_contract.definedEnums().front(), "User-defined types not supported.");
+ if (!_contract.events().empty())
+ error(*_contract.events().front(), "Events not supported.");
+ if (!_contract.functionModifiers().empty())
+ error(*_contract.functionModifiers().front(), "Modifiers not supported.");
+
+ ASTNode::listAccept(_contract.definedFunctions(), *this);
+
+ return false;
+}
+
+void Why3Translator::endVisit(ContractDefinition const& _contract)
+{
+ m_stateVariables.clear();
+ addSourceFromDocStrings(_contract.annotation());
+ unindent();
+ addLine("end");
+}
+
+bool Why3Translator::visit(FunctionDefinition const& _function)
+{
+ if (!_function.isImplemented())
+ {
+ error(_function, "Unimplemented functions not supported.");
+ return false;
+ }
+ if (_function.name().empty())
+ {
+ error(_function, "Fallback functions not supported.");
+ return false;
+ }
+ if (!_function.modifiers().empty())
+ {
+ error(_function, "Modifiers not supported.");
+ return false;
+ }
+
+ m_localVariables.clear();
+ for (auto const& var: _function.parameters())
+ m_localVariables[var->name()] = var.get();
+ for (auto const& var: _function.returnParameters())
+ m_localVariables[var->name()] = var.get();
+ for (auto const& var: _function.localVariables())
+ m_localVariables[var->name()] = var;
+
+ add("let rec _" + _function.name());
+ add(" (state: state)");
+ for (auto const& param: _function.parameters())
+ {
+ string paramType = toFormalType(*param->annotation().type);
+ if (paramType.empty())
+ error(*param, "Parameter type not supported.");
+ if (param->name().empty())
+ error(*param, "Anonymous function parameters not supported.");
+ add(" (arg_" + param->name() + ": " + paramType + ")");
+ }
+ add(":");
+
+ indent();
+ indent();
+ string retString = "(";
+ for (auto const& retParam: _function.returnParameters())
+ {
+ string paramType = toFormalType(*retParam->annotation().type);
+ if (paramType.empty())
+ error(*retParam, "Parameter type not supported.");
+ if (retString.size() != 1)
+ retString += ", ";
+ retString += paramType;
+ }
+ add(retString + ")");
+ unindent();
+
+ addSourceFromDocStrings(_function.annotation());
+
+ addLine("=");
+
+ // initialise local variables
+ for (auto const& variable: _function.parameters())
+ addLine("let _" + variable->name() + " = ref arg_" + variable->name() + " in");
+ for (auto const& variable: _function.returnParameters())
+ {
+ if (variable->name().empty())
+ error(*variable, "Unnamed return variables not yet supported.");
+ string varType = toFormalType(*variable->annotation().type);
+ addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
+ }
+ for (VariableDeclaration const* variable: _function.localVariables())
+ {
+ if (variable->name().empty())
+ error(*variable, "Unnamed variables not yet supported.");
+ string varType = toFormalType(*variable->annotation().type);
+ addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
+ }
+ addLine("try");
+
+ _function.body().accept(*this);
+ add(";");
+ addLine("raise Ret");
+
+ string retVals;
+ for (auto const& variable: _function.returnParameters())
+ {
+ if (!retVals.empty())
+ retVals += ", ";
+ retVals += "!_" + variable->name();
+ }
+ addLine("with Ret -> (" + retVals + ")");
+ newLine();
+ unindent();
+ addLine("end");
+ addLine("");
+ return false;
+}
+
+void Why3Translator::endVisit(FunctionDefinition const&)
+{
+ m_localVariables.clear();
+}
+
+bool Why3Translator::visit(Block const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+ add("begin");
+ indent();
+ for (size_t i = 0; i < _node.statements().size(); ++i)
+ {
+ _node.statements()[i]->accept(*this);
+ if (i != _node.statements().size() - 1)
+ {
+ auto it = m_lines.end() - 1;
+ while (it != m_lines.begin() && it->contents.empty())
+ --it;
+ if (!boost::algorithm::ends_with(it->contents, "begin"))
+ it->contents += ";";
+ }
+ newLine();
+ }
+ unindent();
+ add("end");
+ return false;
+}
+
+bool Why3Translator::visit(IfStatement const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+
+ add("if ");
+ _node.condition().accept(*this);
+ add(" then");
+ visitIndentedUnlessBlock(_node.trueStatement());
+ if (_node.falseStatement())
+ {
+ newLine();
+ add("else");
+ visitIndentedUnlessBlock(*_node.falseStatement());
+ }
+ return false;
+}
+
+bool Why3Translator::visit(WhileStatement const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+
+ add("while ");
+ _node.condition().accept(*this);
+ newLine();
+ add("do");
+ visitIndentedUnlessBlock(_node.body());
+ add("done");
+ return false;
+}
+
+bool Why3Translator::visit(Return const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+
+ if (_node.expression())
+ {
+ solAssert(!!_node.annotation().functionReturnParameters, "");
+ auto const& params = _node.annotation().functionReturnParameters->parameters();
+ if (params.size() != 1)
+ {
+ error(_node, "Directly returning tuples not supported. Rather assign to return variable.");
+ return false;
+ }
+ add("begin _" + params.front()->name() + " := ");
+ _node.expression()->accept(*this);
+ add("; raise Ret end");
+ }
+ else
+ add("raise Ret");
+ return false;
+}
+
+bool Why3Translator::visit(VariableDeclarationStatement const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+
+ if (_node.declarations().size() != 1)
+ {
+ error(_node, "Multiple variables not supported.");
+ return false;
+ }
+ if (_node.initialValue())
+ {
+ add("_" + _node.declarations().front()->name() + " := ");
+ _node.initialValue()->accept(*this);
+ }
+ return false;
+}
+
+bool Why3Translator::visit(ExpressionStatement const& _node)
+{
+ addSourceFromDocStrings(_node.annotation());
+ return true;
+}
+
+bool Why3Translator::visit(Assignment const& _node)
+{
+ if (_node.assignmentOperator() != Token::Assign)
+ error(_node, "Compound assignment not supported.");
+
+ _node.leftHandSide().accept(*this);
+ add(" := ");
+ _node.rightHandSide().accept(*this);
+
+ return false;
+}
+
+bool Why3Translator::visit(TupleExpression const& _node)
+{
+ if (_node.components().size() != 1)
+ error(_node, "Only tuples with exactly one component supported.");
+ add("(");
+ return true;
+}
+
+bool Why3Translator::visit(UnaryOperation const& _unaryOperation)
+{
+ if (toFormalType(*_unaryOperation.annotation().type).empty())
+ error(_unaryOperation, "Type not supported.");
+
+ switch (_unaryOperation.getOperator())
+ {
+ case Token::Not: // !
+ add("(not ");
+ break;
+ default:
+ error(_unaryOperation, "Operator not supported.");
+ break;
+ }
+
+ _unaryOperation.subExpression().accept(*this);
+ add(")");
+
+ return false;
+}
+
+bool Why3Translator::visit(BinaryOperation const& _binaryOperation)
+{
+ Expression const& leftExpression = _binaryOperation.leftExpression();
+ Expression const& rightExpression = _binaryOperation.rightExpression();
+ solAssert(!!_binaryOperation.annotation().commonType, "");
+ Type const& commonType = *_binaryOperation.annotation().commonType;
+ Token::Value const c_op = _binaryOperation.getOperator();
+
+ if (commonType.category() == Type::Category::IntegerConstant)
+ {
+ add("(of_int " + toString(commonType.literalValue(nullptr)) + ")");
+ return false;
+ }
+ static const map<Token::Value, char const*> optrans({
+ {Token::And, " && "},
+ {Token::Or, " || "},
+ {Token::BitOr, " lor "},
+ {Token::BitXor, " lxor "},
+ {Token::BitAnd, " land "},
+ {Token::Add, " + "},
+ {Token::Sub, " - "},
+ {Token::Mul, " * "},
+ {Token::Div, " / "},
+ {Token::Mod, " mod "},
+ {Token::Equal, " = "},
+ {Token::NotEqual, " <> "},
+ {Token::LessThan, " < "},
+ {Token::GreaterThan, " > "},
+ {Token::LessThanOrEqual, " <= "},
+ {Token::GreaterThanOrEqual, " >= "}
+ });
+ if (!optrans.count(c_op))
+ error(_binaryOperation, "Operator not supported.");
+
+ add("(");
+ leftExpression.accept(*this);
+ add(optrans.at(c_op));
+ rightExpression.accept(*this);
+ add(")");
+
+ return false;
+}
+
+bool Why3Translator::visit(FunctionCall const& _node)
+{
+ if (_node.annotation().isTypeConversion || _node.annotation().isStructConstructorCall)
+ {
+ error(_node, "Only ordinary function calls supported.");
+ return true;
+ }
+ FunctionType const& function = dynamic_cast<FunctionType const&>(*_node.expression().annotation().type);
+ switch (function.location())
+ {
+ case FunctionType::Location::AddMod:
+ case FunctionType::Location::MulMod:
+ {
+ //@todo require that third parameter is not zero
+ add("(of_int (mod (Int.(");
+ add(function.location() == FunctionType::Location::AddMod ? "+" : "*");
+ add(") (to_int ");
+ _node.arguments().at(0)->accept(*this);
+ add(") (to_int ");
+ _node.arguments().at(1)->accept(*this);
+ add(")) (to_int ");
+ _node.arguments().at(2)->accept(*this);
+ add(")))");
+ return false;
+ }
+ case FunctionType::Location::Internal:
+ {
+ if (!_node.names().empty())
+ {
+ error(_node, "Function calls with named arguments not supported.");
+ return true;
+ }
+
+ //@TODO check type conversions
+
+ add("(");
+ _node.expression().accept(*this);
+ add(" state");
+ for (auto const& arg: _node.arguments())
+ {
+ add(" ");
+ arg->accept(*this);
+ }
+ add(")");
+ return false;
+ }
+ default:
+ error(_node, "Only internal function calls supported.");
+ return true;
+ }
+}
+
+bool Why3Translator::visit(MemberAccess const& _node)
+{
+ if (
+ _node.expression().annotation().type->category() == Type::Category::Array &&
+ _node.memberName() == "length" &&
+ !_node.annotation().lValueRequested
+ )
+ {
+ add("(of_int ");
+ _node.expression().accept(*this);
+ add(".length");
+ add(")");
+ }
+ else
+ error(_node, "Only read-only length access for arrays supported.");
+ return false;
+}
+
+bool Why3Translator::visit(IndexAccess const& _node)
+{
+ auto baseType = dynamic_cast<ArrayType const*>(_node.baseExpression().annotation().type.get());
+ if (!baseType)
+ {
+ error(_node, "Index access only supported for arrays.");
+ return true;
+ }
+ if (_node.annotation().lValueRequested)
+ {
+ error(_node, "Assignment to array elements not supported.");
+ return true;
+ }
+ add("(");
+ _node.baseExpression().accept(*this);
+ add("[to_int ");
+ _node.indexExpression()->accept(*this);
+ add("]");
+ add(")");
+
+ return false;
+}
+
+bool Why3Translator::visit(Identifier const& _identifier)
+{
+ Declaration const* declaration = _identifier.annotation().referencedDeclaration;
+ if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
+ add("_" + functionDef->name());
+ else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ {
+ bool isStateVar = isStateVariable(variable);
+ bool lvalue = _identifier.annotation().lValueRequested;
+ if (!lvalue)
+ add("!(");
+ if (isStateVar)
+ add("state.");
+ add("_" + variable->name());
+ if (!lvalue)
+ add(")");
+ }
+ else
+ error(_identifier, "Not supported.");
+ return false;
+}
+
+bool Why3Translator::visit(Literal const& _literal)
+{
+ TypePointer type = _literal.annotation().type;
+ switch (type->category())
+ {
+ case Type::Category::Bool:
+ if (type->literalValue(&_literal) == 0)
+ add("false");
+ else
+ add("true");
+ break;
+ case Type::Category::IntegerConstant:
+ add("(of_int " + toString(type->literalValue(&_literal)) + ")");
+ break;
+ default:
+ error(_literal, "Not supported.");
+ }
+ return false;
+}
+
+bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const
+{
+ return contains(m_stateVariables, _var);
+}
+
+bool Why3Translator::isStateVariable(string const& _name) const
+{
+ for (auto const& var: m_stateVariables)
+ if (var->name() == _name)
+ return true;
+ return false;
+}
+
+bool Why3Translator::isLocalVariable(VariableDeclaration const* _var) const
+{
+ for (auto const& var: m_localVariables)
+ if (var.second == _var)
+ return true;
+ return false;
+}
+
+bool Why3Translator::isLocalVariable(string const& _name) const
+{
+ return m_localVariables.count(_name);
+}
+
+void Why3Translator::visitIndentedUnlessBlock(Statement const& _statement)
+{
+ bool isBlock = !!dynamic_cast<Block const*>(&_statement);
+ if (isBlock)
+ newLine();
+ else
+ indent();
+ _statement.accept(*this);
+ if (isBlock)
+ newLine();
+ else
+ unindent();
+}
+
+void Why3Translator::addSourceFromDocStrings(DocumentedAnnotation const& _annotation)
+{
+ auto why3Range = _annotation.docTags.equal_range("why3");
+ for (auto i = why3Range.first; i != why3Range.second; ++i)
+ addLine(transformVariableReferences(i->second.content));
+}
+
+string Why3Translator::transformVariableReferences(string const& _annotation)
+{
+ string ret;
+ auto pos = _annotation.begin();
+ while (true)
+ {
+ auto hash = find(pos, _annotation.end(), '#');
+ ret.append(pos, hash);
+ if (hash == _annotation.end())
+ break;
+
+ auto hashEnd = find_if(hash + 1, _annotation.end(), [](char _c)
+ {
+ return
+ (_c != '_' && _c != '$') &&
+ !('a' <= _c && _c <= 'z') &&
+ !('A' <= _c && _c <= 'Z') &&
+ !('0' <= _c && _c <= '9');
+ });
+ string varName(hash + 1, hashEnd);
+ if (isLocalVariable(varName))
+ ret += "(to_int !_" + varName + ")";
+ else if (isStateVariable(varName))
+ ret += "(to_int !(state._" + varName + "))";
+ else if (varName == "result") //@todo actually use the name of the return parameters
+ ret += "(to_int result)";
+ else
+ ret.append(hash, hashEnd);
+
+ pos = hashEnd;
+ }
+ return ret;
+}
+
diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h
new file mode 100644
index 00000000..588b6d80
--- /dev/null
+++ b/libsolidity/formal/Why3Translator.h
@@ -0,0 +1,134 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Component that translates Solidity code into the why3 programming language.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <string>
+
+namespace dev
+{
+namespace solidity
+{
+
+class SourceUnit;
+
+/**
+ * Simple translator from Solidity to Why3.
+ *
+ * @todo detect side effects in sub-expressions and limit them to one per statement.
+ * @todo `x = y = z`
+ * @todo implicit and explicit type conversion
+ */
+class Why3Translator: private ASTConstVisitor
+{
+public:
+ Why3Translator(ErrorList& _errors): m_lines(std::vector<Line>{{std::string(), 0}}), m_errors(_errors) {}
+
+ /// Appends formalisation of the given source unit to the output.
+ /// @returns false on error.
+ bool process(SourceUnit const& _source);
+
+ std::string translation() const;
+
+private:
+ /// Creates an error and adds it to errors list.
+ void error(ASTNode const& _node, std::string const& _description);
+ /// Reports a fatal error and throws.
+ void fatalError(ASTNode const& _node, std::string const& _description);
+
+ /// Appends imports and constants use throughout the formal code.
+ void appendPreface();
+
+ /// @returns a string representation of the corresponding formal type or the empty string
+ /// if the type is not supported.
+ std::string toFormalType(Type const& _type) const;
+
+ void indent() { newLine(); m_lines.back().indentation++; }
+ void unindent();
+ void addLine(std::string const& _line);
+ void add(std::string const& _str);
+ void newLine();
+ void appendSemicolon();
+
+ virtual bool visit(SourceUnit const&) override { return true; }
+ virtual bool visit(ContractDefinition const& _contract) override;
+ virtual void endVisit(ContractDefinition const& _contract) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+ virtual void endVisit(FunctionDefinition const& _function) override;
+ virtual bool visit(Block const&) override;
+ virtual bool visit(IfStatement const& _node) override;
+ virtual bool visit(WhileStatement const& _node) override;
+ virtual bool visit(Return const& _node) override;
+ virtual bool visit(VariableDeclarationStatement const& _node) override;
+ virtual bool visit(ExpressionStatement const&) override;
+ virtual bool visit(Assignment const& _node) override;
+ virtual bool visit(TupleExpression const& _node) override;
+ virtual void endVisit(TupleExpression const&) override { add(")"); }
+ virtual bool visit(UnaryOperation const& _node) override;
+ virtual bool visit(BinaryOperation const& _node) override;
+ virtual bool visit(FunctionCall const& _node) override;
+ virtual bool visit(MemberAccess const& _node) override;
+ virtual bool visit(IndexAccess const& _node) override;
+ virtual bool visit(Identifier const& _node) override;
+ virtual bool visit(Literal const& _node) override;
+
+ virtual bool visitNode(ASTNode const& _node) override
+ {
+ error(_node, "Code not supported for formal verification.");
+ return false;
+ }
+
+ bool isStateVariable(VariableDeclaration const* _var) const;
+ bool isStateVariable(std::string const& _name) const;
+ bool isLocalVariable(VariableDeclaration const* _var) const;
+ bool isLocalVariable(std::string const& _name) const;
+
+ /// Visits the givin statement and indents it unless it is a block
+ /// (which does its own indentation).
+ void visitIndentedUnlessBlock(Statement const& _statement);
+
+ void addSourceFromDocStrings(DocumentedAnnotation const& _annotation);
+ /// Transforms substring like `#varName` and `#stateVarName` to code that evaluates to their value.
+ std::string transformVariableReferences(std::string const& _annotation);
+
+ /// True if we have already seen a contract. For now, only a single contract
+ /// is supported.
+ bool m_seenContract = false;
+ bool m_errorOccured = false;
+
+ std::vector<VariableDeclaration const*> m_stateVariables;
+ std::map<std::string, VariableDeclaration const*> m_localVariables;
+
+ struct Line
+ {
+ std::string contents;
+ unsigned indentation;
+ };
+ std::vector<Line> m_lines;
+ ErrorList& m_errors;
+};
+
+
+}
+}
diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt
new file mode 100644
index 00000000..08a74f45
--- /dev/null
+++ b/libsolidity/grammar.txt
@@ -0,0 +1,48 @@
+ContractDefinition = ( 'contract' | 'library' ) Identifier
+ ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
+ '{' ContractPart* '}'
+ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition
+
+InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
+StructDefinition = 'struct' Identifier '{'
+ ( VariableDeclaration (';' VariableDeclaration)* )? '}
+StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';'
+ModifierDefinition = 'modifier' Identifier ParameterList? Block
+FunctionDefinition = 'function' Identifier ParameterList
+ ( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )*
+ ( 'returns' ParameterList )? Block
+
+EnumValue = Identifier
+EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}'
+ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
+// semantic restriction: mappings and structs (recursively) containing mappings
+// are not allowed in argument lists
+VariableDeclaration = TypeName Identifier
+TypeName = ElementaryTypeName | Identifier | Mapping | ArrayTypeName
+Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
+ArrayTypeName = TypeName '[' (Expression)? ']'
+
+Block = '{' Statement* '}'
+Statement = IfStatement | WhileStatement | Block |
+ ( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';'
+
+ExpressionStatement = Expression
+IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
+WhileStatement = 'while' '(' Expression ')' Statement
+VardefOrExprStmt = Variabledefinition | ExpressionStatement
+ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
+Continue = 'continue' ';'
+Break = 'break' ';'
+Return = 'return' Expression? ';'
+Throw = 'throw' Expression? ';'
+VariableDefinition = VariableDeclaration ( = Expression )? ';'
+
+Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess |
+ MemberAccess | PrimaryExpression
+// The expression syntax is actually much more complicated
+Assignment = Expression (AssignmentOp Expression)
+FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
+NewExpression = 'new' Identifier
+MemberAccess = Expression '.' Identifier
+IndexAccess = Expression '[' (Expresison)? ']'
+PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
new file mode 100644
index 00000000..83459183
--- /dev/null
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -0,0 +1,551 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Full-stack compiler that converts a source code string to bytecode.
+ */
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/GlobalContext.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/analysis/DocStringAnalyser.h>
+#include <libsolidity/analysis/SyntaxChecker.h>
+#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/InterfaceHandler.h>
+#include <libsolidity/formal/Why3Translator.h>
+
+#include <libdevcore/SHA3.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+const map<string, string> StandardSources = map<string, string>{
+ {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
+ {"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
+ {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,bytes3 name,uint256 denom){}function register(bytes3 name,uint256 denom){}function unregister(){}})"},
+ {"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
+ {"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
+ {"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
+ {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(bytes32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
+ {"NameReg", R"(contract NameReg{function register(bytes32 name){}function addressOf(bytes32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(bytes32 name){}})"},
+ {"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
+ {"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
+ {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
+};
+
+CompilerStack::CompilerStack(bool _addStandardSources, ReadFileCallback const& _readFile):
+ m_readFile(_readFile), m_parseSuccessful(false)
+{
+ if (_addStandardSources)
+ addSources(StandardSources, true); // add them as libraries
+}
+
+void CompilerStack::reset(bool _keepSources, bool _addStandardSources)
+{
+ m_parseSuccessful = false;
+ if (_keepSources)
+ for (auto sourcePair: m_sources)
+ sourcePair.second.reset();
+ else
+ {
+ m_sources.clear();
+ if (_addStandardSources)
+ addSources(StandardSources, true);
+ }
+ m_globalContext.reset();
+ m_sourceOrder.clear();
+ m_contracts.clear();
+ m_errors.clear();
+}
+
+bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
+{
+ bool existed = m_sources.count(_name) != 0;
+ reset(true);
+ m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
+ m_sources[_name].isLibrary = _isLibrary;
+ return existed;
+}
+
+void CompilerStack::setSource(string const& _sourceCode)
+{
+ reset();
+ addSource("", _sourceCode);
+}
+
+bool CompilerStack::parse()
+{
+ //reset
+ m_errors.clear();
+ m_parseSuccessful = false;
+
+ vector<string> sourcesToParse;
+ for (auto const& s: m_sources)
+ sourcesToParse.push_back(s.first);
+ map<string, SourceUnit const*> sourceUnitsByName;
+ for (size_t i = 0; i < sourcesToParse.size(); ++i)
+ {
+ string const& path = sourcesToParse[i];
+ Source& source = m_sources[path];
+ source.scanner->reset();
+ source.ast = Parser(m_errors).parse(source.scanner);
+ sourceUnitsByName[path] = source.ast.get();
+ if (!source.ast)
+ solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
+ else
+ {
+ source.ast->annotation().path = path;
+ for (auto const& newSource: loadMissingSources(*source.ast, path))
+ {
+ string const& newPath = newSource.first;
+ string const& newContents = newSource.second;
+ m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents), newPath);
+ sourcesToParse.push_back(newPath);
+ }
+ }
+ }
+ if (!Error::containsOnlyWarnings(m_errors))
+ // errors while parsing. sould stop before type checking
+ return false;
+
+ resolveImports();
+
+ bool noErrors = true;
+ SyntaxChecker syntaxChecker(m_errors);
+ for (Source const* source: m_sourceOrder)
+ if (!syntaxChecker.checkSyntax(*source->ast))
+ noErrors = false;
+
+ DocStringAnalyser docStringAnalyser(m_errors);
+ for (Source const* source: m_sourceOrder)
+ if (!docStringAnalyser.analyseDocStrings(*source->ast))
+ noErrors = false;
+
+ m_globalContext = make_shared<GlobalContext>();
+ NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors);
+ for (Source const* source: m_sourceOrder)
+ if (!resolver.registerDeclarations(*source->ast))
+ return false;
+
+ for (Source const* source: m_sourceOrder)
+ if (!resolver.performImports(*source->ast, sourceUnitsByName))
+ return false;
+
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ m_globalContext->setCurrentContract(*contract);
+ if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false;
+ if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false;
+ if (!resolver.resolveNamesAndTypes(*contract)) return false;
+ m_contracts[contract->name()].contract = contract;
+ }
+
+ if (!checkLibraryNameClashes())
+ noErrors = false;
+
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ m_globalContext->setCurrentContract(*contract);
+ resolver.updateDeclaration(*m_globalContext->currentThis());
+ TypeChecker typeChecker(m_errors);
+ if (typeChecker.checkTypeRequirements(*contract))
+ {
+ contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract));
+ contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract));
+ }
+ else
+ noErrors = false;
+
+ m_contracts[contract->name()].contract = contract;
+ }
+ m_parseSuccessful = noErrors;
+ return m_parseSuccessful;
+}
+
+bool CompilerStack::parse(string const& _sourceCode)
+{
+ setSource(_sourceCode);
+ return parse();
+}
+
+vector<string> CompilerStack::contractNames() const
+{
+ if (!m_parseSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ vector<string> contractNames;
+ for (auto const& contract: m_contracts)
+ contractNames.push_back(contract.first);
+ return contractNames;
+}
+
+
+bool CompilerStack::compile(bool _optimize, unsigned _runs)
+{
+ if (!m_parseSuccessful)
+ if (!parse())
+ return false;
+
+ map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
+ compileContract(_optimize, _runs, *contract, compiledContracts);
+ return true;
+}
+
+bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
+{
+ return parse(_sourceCode) && compile(_optimize);
+}
+
+void CompilerStack::link(const std::map<string, h160>& _libraries)
+{
+ for (auto& contract: m_contracts)
+ {
+ contract.second.object.link(_libraries);
+ contract.second.runtimeObject.link(_libraries);
+ contract.second.cloneObject.link(_libraries);
+ }
+}
+
+bool CompilerStack::prepareFormalAnalysis()
+{
+ Why3Translator translator(m_errors);
+ for (Source const* source: m_sourceOrder)
+ if (!translator.process(*source->ast))
+ return false;
+
+ m_formalTranslation = translator.translation();
+
+ return true;
+}
+
+eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
+{
+ Contract const& currentContract = contract(_contractName);
+ return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr;
+}
+
+eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
+{
+ Contract const& currentContract = contract(_contractName);
+ return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
+}
+
+eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
+{
+ return contract(_contractName).object;
+}
+
+eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
+{
+ return contract(_contractName).runtimeObject;
+}
+
+eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const
+{
+ return contract(_contractName).cloneObject;
+}
+
+dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
+{
+ auto const& obj = runtimeObject(_contractName);
+ if (obj.bytecode.empty() || !obj.linkReferences.empty())
+ return dev::h256();
+ else
+ return dev::sha3(obj.bytecode);
+}
+
+Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
+{
+ Contract const& currentContract = contract(_contractName);
+ if (currentContract.compiler)
+ return currentContract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat);
+ else
+ {
+ _outStream << "Contract not fully implemented" << endl;
+ return Json::Value();
+ }
+}
+
+string const& CompilerStack::interface(string const& _contractName) const
+{
+ return metadata(_contractName, DocumentationType::ABIInterface);
+}
+
+string const& CompilerStack::solidityInterface(string const& _contractName) const
+{
+ return metadata(_contractName, DocumentationType::ABISolidityInterface);
+}
+
+string const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
+{
+ if (!m_parseSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+
+ std::unique_ptr<string const>* doc;
+ Contract const& currentContract = contract(_contractName);
+
+ // checks wheather we already have the documentation
+ switch (_type)
+ {
+ case DocumentationType::NatspecUser:
+ doc = &currentContract.userDocumentation;
+ break;
+ case DocumentationType::NatspecDev:
+ doc = &currentContract.devDocumentation;
+ break;
+ case DocumentationType::ABIInterface:
+ doc = &currentContract.interface;
+ break;
+ case DocumentationType::ABISolidityInterface:
+ doc = &currentContract.solidityInterface;
+ break;
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
+ }
+
+ // caches the result
+ if (!*doc)
+ doc->reset(new string(InterfaceHandler::documentation(*currentContract.contract, _type)));
+
+ return *(*doc);
+}
+
+Scanner const& CompilerStack::scanner(string const& _sourceName) const
+{
+ return *source(_sourceName).scanner;
+}
+
+SourceUnit const& CompilerStack::ast(string const& _sourceName) const
+{
+ return *source(_sourceName).ast;
+}
+
+ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
+{
+ return *contract(_contractName).contract;
+}
+
+size_t CompilerStack::functionEntryPoint(
+ std::string const& _contractName,
+ FunctionDefinition const& _function
+) const
+{
+ shared_ptr<Compiler> const& compiler = contract(_contractName).compiler;
+ if (!compiler)
+ return 0;
+ eth::AssemblyItem tag = compiler->functionEntryLabel(_function);
+ if (tag.type() == eth::UndefinedItem)
+ return 0;
+ eth::AssemblyItems const& items = compiler->runtimeAssemblyItems();
+ for (size_t i = 0; i < items.size(); ++i)
+ if (items.at(i).type() == eth::Tag && items.at(i).data() == tag.data())
+ return i;
+ return 0;
+}
+
+tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
+{
+ int startLine;
+ int startColumn;
+ int endLine;
+ int endColumn;
+ tie(startLine, startColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.start);
+ tie(endLine, endColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.end);
+
+ return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
+}
+
+StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _path)
+{
+ StringMap newSources;
+ for (auto const& node: _ast.nodes())
+ if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
+ {
+ string path = absolutePath(import->path(), _path);
+ import->annotation().absolutePath = path;
+ if (m_sources.count(path) || newSources.count(path))
+ continue;
+ string contents;
+ string errorMessage;
+ if (!m_readFile)
+ errorMessage = "File not supplied initially.";
+ else
+ tie(contents, errorMessage) = m_readFile(path);
+ if (!errorMessage.empty())
+ {
+ auto err = make_shared<Error>(Error::Type::ParserError);
+ *err <<
+ errinfo_sourceLocation(import->location()) <<
+ errinfo_comment("Source not found: " + errorMessage);
+ m_errors.push_back(std::move(err));
+ continue;
+ }
+ else
+ newSources[path] = contents;
+ }
+ return newSources;
+}
+
+void CompilerStack::resolveImports()
+{
+ // topological sorting (depth first search) of the import graph, cutting potential cycles
+ vector<Source const*> sourceOrder;
+ set<Source const*> sourcesSeen;
+
+ function<void(Source const*)> toposort = [&](Source const* _source)
+ {
+ if (sourcesSeen.count(_source))
+ return;
+ sourcesSeen.insert(_source);
+ for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
+ if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
+ {
+ string const& path = import->annotation().absolutePath;
+ solAssert(!path.empty(), "");
+ solAssert(m_sources.count(path), "");
+ import->annotation().sourceUnit = m_sources[path].ast.get();
+ toposort(&m_sources[path]);
+ }
+ sourceOrder.push_back(_source);
+ };
+
+ for (auto const& sourcePair: m_sources)
+ if (!sourcePair.second.isLibrary)
+ toposort(&sourcePair.second);
+
+ swap(m_sourceOrder, sourceOrder);
+}
+
+bool CompilerStack::checkLibraryNameClashes()
+{
+ bool clashFound = false;
+ map<string, SourceLocation> libraries;
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ if (contract->isLibrary())
+ {
+ if (libraries.count(contract->name()))
+ {
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err <<
+ errinfo_sourceLocation(contract->location()) <<
+ errinfo_comment(
+ "Library \"" + contract->name() + "\" declared twice "
+ "(will create ambiguities during linking)."
+ ) <<
+ errinfo_secondarySourceLocation(SecondarySourceLocation().append(
+ "The other declaration is here:", libraries[contract->name()]
+ ));
+
+ m_errors.push_back(err);
+ clashFound = true;
+ }
+ else
+ libraries[contract->name()] = contract->location();
+ }
+ return !clashFound;
+}
+
+string CompilerStack::absolutePath(string const& _path, string const& _reference) const
+{
+ // Anything that does not start with `.` is an absolute path.
+ if (_path.empty() || _path.front() != '.')
+ return _path;
+ using path = boost::filesystem::path;
+ path p(_path);
+ path result(_reference);
+ result.remove_filename();
+ for (path::iterator it = p.begin(); it != p.end(); ++it)
+ if (*it == "..")
+ result = result.parent_path();
+ else if (*it != ".")
+ result /= *it;
+ return result.string();
+}
+
+void CompilerStack::compileContract(
+ bool _optimize,
+ unsigned _runs,
+ ContractDefinition const& _contract,
+ map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+)
+{
+ if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented)
+ return;
+ for (auto const* dependency: _contract.annotation().contractDependencies)
+ compileContract(_optimize, _runs, *dependency, _compiledContracts);
+
+ shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
+ compiler->compileContract(_contract, _compiledContracts);
+ Contract& compiledContract = m_contracts.at(_contract.name());
+ compiledContract.compiler = compiler;
+ compiledContract.object = compiler->assembledObject();
+ compiledContract.runtimeObject = compiler->runtimeObject();
+ _compiledContracts[compiledContract.contract] = &compiler->assembly();
+
+ Compiler cloneCompiler(_optimize, _runs);
+ cloneCompiler.compileClone(_contract, _compiledContracts);
+ compiledContract.cloneObject = cloneCompiler.assembledObject();
+}
+
+std::string CompilerStack::defaultContractName() const
+{
+ return contract("").contract->name();
+}
+
+CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
+{
+ if (m_contracts.empty())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
+ string contractName = _contractName;
+ if (_contractName.empty())
+ // try to find some user-supplied contract
+ for (auto const& it: m_sources)
+ if (!StandardSources.count(it.first))
+ for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
+ if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
+ contractName = contract->name();
+ auto it = m_contracts.find(contractName);
+ if (it == m_contracts.end())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
+ return it->second;
+}
+
+CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const
+{
+ auto it = m_sources.find(_sourceName);
+ if (it == m_sources.end())
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
+
+ return it->second;
+}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
new file mode 100644
index 00000000..c7f98184
--- /dev/null
+++ b/libsolidity/interface/CompilerStack.h
@@ -0,0 +1,240 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Full-stack compiler that converts a source code string to bytecode.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <string>
+#include <memory>
+#include <vector>
+#include <functional>
+#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/LinkerObject.h>
+#include <libsolidity/interface/Exceptions.h>
+
+namespace dev
+{
+
+namespace eth
+{
+class Assembly;
+class AssemblyItem;
+using AssemblyItems = std::vector<AssemblyItem>;
+}
+
+namespace solidity
+{
+
+// forward declarations
+class Scanner;
+class ContractDefinition;
+class FunctionDefinition;
+class SourceUnit;
+class Compiler;
+class GlobalContext;
+class InterfaceHandler;
+class Error;
+
+enum class DocumentationType: uint8_t
+{
+ NatspecUser = 1,
+ NatspecDev,
+ ABIInterface,
+ ABISolidityInterface
+};
+
+/**
+ * Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
+ * It holds state and can be used to either step through the compilation stages (and abort e.g.
+ * before compilation to bytecode) or run the whole compilation in one call.
+ */
+class CompilerStack: boost::noncopyable
+{
+public:
+ /// File reading callback, should return a pair of content and error message (exactly one nonempty)
+ /// for a given path.
+ using ReadFileCallback = std::function<std::pair<std::string, std::string>(std::string const&)>;
+
+ /// Creates a new compiler stack.
+ /// @param _readFile callback to used to read files for import statements. Should return
+ /// @param _addStandardSources Adds standard sources if @a _addStandardSources.
+ explicit CompilerStack(bool _addStandardSources = true, ReadFileCallback const& _readFile = ReadFileCallback());
+
+ /// Resets the compiler to a state where the sources are not parsed or even removed.
+ void reset(bool _keepSources = false, bool _addStandardSources = true);
+
+ /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
+ /// @returns true if a source object by the name already existed and was replaced.
+ void addSources(StringMap const& _nameContents, bool _isLibrary = false)
+ {
+ for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary);
+ }
+ bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
+ void setSource(std::string const& _sourceCode);
+ /// Parses all source units that were added
+ /// @returns false on error.
+ bool parse();
+ /// Sets the given source code as the only source unit apart from standard sources and parses it.
+ /// @returns false on error.
+ bool parse(std::string const& _sourceCode);
+ /// @returns a list of the contract names in the sources.
+ std::vector<std::string> contractNames() const;
+ std::string defaultContractName() const;
+
+ /// Compiles the source units that were previously added and parsed.
+ /// @returns false on error.
+ bool compile(bool _optimize = false, unsigned _runs = 200);
+ /// Parses and compiles the given source code.
+ /// @returns false on error.
+ bool compile(std::string const& _sourceCode, bool _optimize = false);
+
+ /// Inserts the given addresses into the linker objects of all compiled contracts.
+ void link(std::map<std::string, h160> const& _libraries);
+
+ /// Tries to translate all source files into a language suitable for formal analysis.
+ /// @returns false on error.
+ bool prepareFormalAnalysis();
+ std::string const& formalTranslation() const { return m_formalTranslation; }
+
+ /// @returns the assembled object for a contract.
+ eth::LinkerObject const& object(std::string const& _contractName = "") const;
+ /// @returns the runtime object for the contract.
+ eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
+ /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL.
+ /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
+ /// substituted by the actual address. Note that this sequence starts end ends in three X
+ /// characters but can contain anything in between.
+ eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const;
+ /// @returns normal contract assembly items
+ eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
+ /// @returns runtime contract assembly items
+ eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const;
+ /// @returns hash of the runtime bytecode for the contract, i.e. the code that is
+ /// returned by the constructor or the zero-h256 if the contract still needs to be linked or
+ /// does not have runtime code.
+ dev::h256 contractCodeHash(std::string const& _contractName = "") const;
+
+ /// Streams a verbose version of the assembly to @a _outStream.
+ /// @arg _sourceCodes is the map of input files to source code strings
+ /// @arg _inJsonFromat shows whether the out should be in Json format
+ /// Prerequisite: Successful compilation.
+ Json::Value streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const;
+
+ /// @returns a string representing the contract interface in JSON.
+ /// Prerequisite: Successful call to parse or compile.
+ std::string const& interface(std::string const& _contractName = "") const;
+ /// @returns a string representing the contract interface in Solidity.
+ /// Prerequisite: Successful call to parse or compile.
+ std::string const& solidityInterface(std::string const& _contractName = "") const;
+ /// @returns a string representing the contract's documentation in JSON.
+ /// Prerequisite: Successful call to parse or compile.
+ /// @param type The type of the documentation to get.
+ /// Can be one of 4 types defined at @c DocumentationType
+ std::string const& metadata(std::string const& _contractName, DocumentationType _type) const;
+
+ /// @returns the previously used scanner, useful for counting lines during error reporting.
+ Scanner const& scanner(std::string const& _sourceName = "") const;
+ /// @returns the parsed source unit with the supplied name.
+ SourceUnit const& ast(std::string const& _sourceName = "") const;
+ /// @returns the parsed contract with the supplied name. Throws an exception if the contract
+ /// does not exist.
+ ContractDefinition const& contractDefinition(std::string const& _contractName) const;
+
+ /// @returns the offset of the entry point of the given function into the list of assembly items
+ /// or zero if it is not found or does not exist.
+ size_t functionEntryPoint(
+ std::string const& _contractName,
+ FunctionDefinition const& _function
+ ) const;
+
+ /// Helper function for logs printing. Do only use in error cases, it's quite expensive.
+ /// line and columns are numbered starting from 1 with following order:
+ /// start line, start column, end line, end column
+ std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
+
+ /// @returns the list of errors that occured during parsing and type checking.
+ ErrorList const& errors() const { return m_errors; }
+
+private:
+ /**
+ * Information pertaining to one source unit, filled gradually during parsing and compilation.
+ */
+ struct Source
+ {
+ std::shared_ptr<Scanner> scanner;
+ std::shared_ptr<SourceUnit> ast;
+ std::string interface;
+ bool isLibrary = false;
+ void reset() { scanner.reset(); ast.reset(); interface.clear(); }
+ };
+
+ struct Contract
+ {
+ ContractDefinition const* contract = nullptr;
+ std::shared_ptr<Compiler> compiler;
+ eth::LinkerObject object;
+ eth::LinkerObject runtimeObject;
+ eth::LinkerObject cloneObject;
+ mutable std::unique_ptr<std::string const> interface;
+ mutable std::unique_ptr<std::string const> solidityInterface;
+ mutable std::unique_ptr<std::string const> userDocumentation;
+ mutable std::unique_ptr<std::string const> devDocumentation;
+ };
+
+ /// Loads the missing sources from @a _ast (named @a _path) using the callback
+ /// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
+ /// @returns the newly loaded sources.
+ StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path);
+ void resolveImports();
+ /// Checks whether there are libraries with the same name, reports that as an error and
+ /// @returns false in this case.
+ bool checkLibraryNameClashes();
+ /// @returns the absolute path corresponding to @a _path relative to @a _reference.
+ std::string absolutePath(std::string const& _path, std::string const& _reference) const;
+ /// Compile a single contract and put the result in @a _compiledContracts.
+ void compileContract(
+ bool _optimize,
+ unsigned _runs,
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+ );
+
+ Contract const& contract(std::string const& _contractName = "") const;
+ Source const& source(std::string const& _sourceName = "") const;
+
+ ReadFileCallback m_readFile;
+ bool m_parseSuccessful;
+ std::map<std::string const, Source> m_sources;
+ std::shared_ptr<GlobalContext> m_globalContext;
+ std::vector<Source const*> m_sourceOrder;
+ std::map<std::string const, Contract> m_contracts;
+ std::string m_formalTranslation;
+ ErrorList m_errors;
+};
+
+}
+}
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
new file mode 100644
index 00000000..6d72520b
--- /dev/null
+++ b/libsolidity/interface/Exceptions.cpp
@@ -0,0 +1,58 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Liana <liana@ethdev.com>
+ * @date 2015
+ * Solidity exception hierarchy.
+ */
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/Utils.h>
+
+using namespace dev;
+using namespace dev::solidity;
+
+Error::Error(Type _type): m_type(_type)
+{
+ switch(m_type)
+ {
+ case Type::DeclarationError:
+ m_typeName = "Declaration Error";
+ break;
+ case Type::DocstringParsingError:
+ m_typeName = "Docstring Parsing Error";
+ break;
+ case Type::ParserError:
+ m_typeName = "Parser Error";
+ break;
+ case Type::SyntaxError:
+ m_typeName = "Syntax Error";
+ break;
+ case Type::TypeError:
+ m_typeName = "Type Error";
+ break;
+ case Type::Why3TranslatorError:
+ m_typeName = "Why3 Translator Error";
+ break;
+ case Type::Warning:
+ m_typeName = "Warning";
+ break;
+ default:
+ solAssert(false, "");
+ break;
+ }
+}
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
new file mode 100644
index 00000000..07835320
--- /dev/null
+++ b/libsolidity/interface/Exceptions.h
@@ -0,0 +1,103 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity exception hierarchy.
+ */
+
+#pragma once
+
+#include <string>
+#include <utility>
+#include <libdevcore/Exceptions.h>
+#include <libevmasm/SourceLocation.h>
+
+namespace dev
+{
+namespace solidity
+{
+class Error;
+using ErrorList = std::vector<std::shared_ptr<Error const>>;
+
+struct CompilerError: virtual Exception {};
+struct InternalCompilerError: virtual Exception {};
+struct FatalError: virtual Exception {};
+
+class Error: virtual public Exception
+{
+public:
+ enum class Type
+ {
+ DeclarationError,
+ DocstringParsingError,
+ ParserError,
+ TypeError,
+ SyntaxError,
+ Why3TranslatorError,
+ Warning
+ };
+
+ explicit Error(Type _type);
+
+ Type type() const { return m_type; }
+ std::string const& typeName() const { return m_typeName; }
+
+ /// helper functions
+ static Error const* containsErrorOfType(ErrorList const& _list, Error::Type _type)
+ {
+ for (auto e: _list)
+ {
+ if (e->type() == _type)
+ return e.get();
+ }
+ return nullptr;
+ }
+ static bool containsOnlyWarnings(ErrorList const& _list)
+ {
+ for (auto e: _list)
+ {
+ if (e->type() != Type::Warning)
+ return false;
+ }
+ return true;
+ }
+private:
+ Type m_type;
+ std::string m_typeName;
+};
+
+
+using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
+
+class SecondarySourceLocation
+{
+public:
+ SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
+ {
+ infos.push_back(std::make_pair(_errMsg, _sourceLocation));
+ return *this;
+ }
+ std::vector<errorSourceLocationInfo> infos;
+};
+
+
+using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>;
+using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>;
+
+}
+}
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
new file mode 100644
index 00000000..d460ba76
--- /dev/null
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -0,0 +1,191 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Gas consumption estimator working alongside the AST.
+ */
+
+#include "GasEstimator.h"
+#include <map>
+#include <functional>
+#include <memory>
+#include <libdevcore/SHA3.h>
+#include <libevmasm/ControlFlowGraph.h>
+#include <libevmasm/KnownState.h>
+#include <libevmasm/PathGasMeter.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::eth;
+using namespace dev::solidity;
+
+GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
+ AssemblyItems const& _items,
+ vector<ASTNode const*> const& _ast
+)
+{
+ solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
+ map<SourceLocation, GasConsumption> particularCosts;
+
+ ControlFlowGraph cfg(_items);
+ for (BasicBlock const& block: cfg.optimisedBlocks())
+ {
+ assertThrow(!!block.startState, OptimizerException, "");
+ GasMeter meter(block.startState->copy());
+ auto const end = _items.begin() + block.end;
+ for (auto iter = _items.begin() + block.begin; iter != end; ++iter)
+ particularCosts[iter->location()] += meter.estimateMax(*iter);
+ }
+
+ set<ASTNode const*> finestNodes = finestNodesAtLocation(_ast);
+ ASTGasConsumptionSelfAccumulated gasCosts;
+ auto onNode = [&](ASTNode const& _node)
+ {
+ if (!finestNodes.count(&_node))
+ return true;
+ gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.location()];
+ return true;
+ };
+ auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ gasCosts[&_parent][1] += gasCosts[&_child][1];
+ };
+ ASTReduce folder(onNode, onEdge);
+ for (ASTNode const* ast: _ast)
+ ast->accept(folder);
+
+ return gasCosts;
+}
+
+map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLevel(
+ ASTGasConsumptionSelfAccumulated const& _gasCosts,
+ vector<ASTNode const*> const& _roots
+)
+{
+ solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, "");
+ // first pass: statementDepth[node] is the distance from the deepend statement to node
+ // in direction of the tree root (or undefined if not possible)
+ map<ASTNode const*, int> statementDepth;
+ auto onNodeFirstPass = [&](ASTNode const& _node)
+ {
+ if (dynamic_cast<Statement const*>(&_node))
+ statementDepth[&_node] = 0;
+ return true;
+ };
+ auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ if (statementDepth.count(&_child))
+ statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1);
+ };
+ ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass);
+ for (ASTNode const* node: _roots)
+ node->accept(firstPass);
+
+ // we use the location of a node if
+ // - its statement depth is 0 or
+ // - its statement depth is undefined but the parent's statement depth is at least 1
+ map<ASTNode const*, GasConsumption> gasCosts;
+ auto onNodeSecondPass = [&](ASTNode const& _node)
+ {
+ return statementDepth.count(&_node);
+ };
+ auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child)
+ {
+ bool useNode = false;
+ if (statementDepth.count(&_child))
+ useNode = statementDepth[&_child] == 0;
+ else
+ useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0;
+ if (useNode)
+ gasCosts[&_child] = _gasCosts.at(&_child)[1];
+ };
+ ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass);
+ for (ASTNode const* node: _roots)
+ node->accept(secondPass);
+ // gasCosts should only contain non-overlapping locations
+ return gasCosts;
+}
+
+GasEstimator::GasConsumption GasEstimator::functionalEstimation(
+ AssemblyItems const& _items,
+ string const& _signature
+)
+{
+ auto state = make_shared<KnownState>();
+
+ if (!_signature.empty())
+ {
+ ExpressionClasses& classes = state->expressionClasses();
+ using Id = ExpressionClasses::Id;
+ using Ids = vector<Id>;
+ Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
+ Id calldata = classes.find(eth::Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
+ classes.forceEqual(hashValue, eth::Instruction::DIV, Ids{
+ calldata,
+ classes.find(u256(1) << (8 * 28))
+ });
+ }
+
+ PathGasMeter meter(_items);
+ return meter.estimateMax(0, state);
+}
+
+GasEstimator::GasConsumption GasEstimator::functionalEstimation(
+ AssemblyItems const& _items,
+ size_t const& _offset,
+ FunctionDefinition const& _function
+)
+{
+ auto state = make_shared<KnownState>();
+
+ unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
+ if (parametersSize > 16)
+ return GasConsumption::infinite();
+
+ // Store an invalid return value on the stack, so that the path estimator breaks upon reaching
+ // the return jump.
+ AssemblyItem invalidTag(PushTag, u256(-0x10));
+ state->feedItem(invalidTag, true);
+ if (parametersSize > 0)
+ state->feedItem(eth::swapInstruction(parametersSize));
+
+ return PathGasMeter(_items).estimateMax(_offset, state);
+}
+
+set<ASTNode const*> GasEstimator::finestNodesAtLocation(
+ vector<ASTNode const*> const& _roots
+)
+{
+ map<SourceLocation, ASTNode const*> locations;
+ set<ASTNode const*> nodes;
+ SimpleASTVisitor visitor(function<bool(ASTNode const&)>(), [&](ASTNode const& _n)
+ {
+ if (!locations.count(_n.location()))
+ {
+ locations[_n.location()] = &_n;
+ nodes.insert(&_n);
+ }
+ });
+
+ for (ASTNode const* root: _roots)
+ root->accept(visitor);
+ return nodes;
+}
diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h
new file mode 100644
index 00000000..518e58e4
--- /dev/null
+++ b/libsolidity/interface/GasEstimator.h
@@ -0,0 +1,85 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Gas consumption estimator working alongside the AST.
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+#include <array>
+#include <libevmasm/GasMeter.h>
+#include <libevmasm/Assembly.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ASTNode;
+class FunctionDefinition;
+
+struct GasEstimator
+{
+public:
+ using GasConsumption = eth::GasMeter::GasConsumption;
+ using ASTGasConsumption = std::map<ASTNode const*, GasConsumption>;
+ using ASTGasConsumptionSelfAccumulated =
+ std::map<ASTNode const*, std::array<GasConsumption, 2>>;
+
+ /// Estimates the gas consumption for every assembly item in the given assembly and stores
+ /// it by source location.
+ /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
+ static ASTGasConsumptionSelfAccumulated structuralEstimation(
+ eth::AssemblyItems const& _items,
+ std::vector<ASTNode const*> const& _ast
+ );
+ /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
+ /// the following source locations are part of the mapping:
+ /// 1. source locations of statements that do not contain other statements
+ /// 2. maximal source locations that do not overlap locations coming from the first rule
+ static ASTGasConsumption breakToStatementLevel(
+ ASTGasConsumptionSelfAccumulated const& _gasCosts,
+ std::vector<ASTNode const*> const& _roots
+ );
+
+ /// @returns the estimated gas consumption by the (public or external) function with the
+ /// given signature. If no signature is given, estimates the maximum gas usage.
+ static GasConsumption functionalEstimation(
+ eth::AssemblyItems const& _items,
+ std::string const& _signature = ""
+ );
+
+ /// @returns the estimated gas consumption by the given function which starts at the given
+ /// offset into the list of assembly items.
+ /// @note this does not work correctly for recursive functions.
+ static GasConsumption functionalEstimation(
+ eth::AssemblyItems const& _items,
+ size_t const& _offset,
+ FunctionDefinition const& _function
+ );
+
+private:
+ /// @returns the set of AST nodes which are the finest nodes at their location.
+ static std::set<ASTNode const*> finestNodesAtLocation(std::vector<ASTNode const*> const& _roots);
+};
+
+}
+}
diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp
new file mode 100644
index 00000000..e254137f
--- /dev/null
+++ b/libsolidity/interface/InterfaceHandler.cpp
@@ -0,0 +1,248 @@
+
+#include <libsolidity/interface/InterfaceHandler.h>
+#include <boost/range/irange.hpp>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/CompilerStack.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+string InterfaceHandler::documentation(
+ ContractDefinition const& _contractDef,
+ DocumentationType _type
+)
+{
+ switch(_type)
+ {
+ case DocumentationType::NatspecUser:
+ return userDocumentation(_contractDef);
+ case DocumentationType::NatspecDev:
+ return devDocumentation(_contractDef);
+ case DocumentationType::ABIInterface:
+ return abiInterface(_contractDef);
+ case DocumentationType::ABISolidityInterface:
+ return ABISolidityInterface(_contractDef);
+ }
+
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
+ return "";
+}
+
+string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
+{
+ Json::Value abi(Json::arrayValue);
+
+ auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
+ {
+ Json::Value params(Json::arrayValue);
+ solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
+ for (unsigned i = 0; i < _paramNames.size(); ++i)
+ {
+ Json::Value param;
+ param["name"] = _paramNames[i];
+ param["type"] = _paramTypes[i];
+ params.append(param);
+ }
+ return params;
+ };
+
+ for (auto it: _contractDef.interfaceFunctions())
+ {
+ auto externalFunctionType = it.second->interfaceFunctionType();
+ Json::Value method;
+ method["type"] = "function";
+ method["name"] = it.second->declaration().name();
+ method["constant"] = it.second->isConstant();
+ method["inputs"] = populateParameters(
+ externalFunctionType->parameterNames(),
+ externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
+ );
+ method["outputs"] = populateParameters(
+ externalFunctionType->returnParameterNames(),
+ externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary())
+ );
+ abi.append(method);
+ }
+ if (_contractDef.constructor())
+ {
+ Json::Value method;
+ method["type"] = "constructor";
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
+ solAssert(!!externalFunction, "");
+ method["inputs"] = populateParameters(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
+ );
+ abi.append(method);
+ }
+
+ for (auto const& it: _contractDef.interfaceEvents())
+ {
+ Json::Value event;
+ event["type"] = "event";
+ event["name"] = it->name();
+ event["anonymous"] = it->isAnonymous();
+ Json::Value params(Json::arrayValue);
+ for (auto const& p: it->parameters())
+ {
+ Json::Value input;
+ input["name"] = p->name();
+ input["type"] = p->annotation().type->canonicalName(false);
+ input["indexed"] = p->isIndexed();
+ params.append(input);
+ }
+ event["inputs"] = params;
+ abi.append(event);
+ }
+ return Json::FastWriter().write(abi);
+}
+
+string InterfaceHandler::ABISolidityInterface(ContractDefinition const& _contractDef)
+{
+ string ret = (_contractDef.isLibrary() ? "library " : "contract ") + _contractDef.name() + "{";
+
+ auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
+ {
+ string ret = "(";
+ for (size_t i = 0; i < _paramNames.size(); ++i)
+ ret += _paramTypes[i] + " " + _paramNames[i] + ",";
+ if (ret.size() != 1)
+ ret.pop_back();
+ return ret + ")";
+ };
+ // If this is a library, include all its enum and struct types. Should be more intelligent
+ // in the future and check what is actually used (it might even use types from other libraries
+ // or contracts or in the global scope).
+ if (_contractDef.isLibrary())
+ {
+ for (auto const& stru: _contractDef.definedStructs())
+ {
+ ret += "struct " + stru->name() + "{";
+ for (ASTPointer<VariableDeclaration> const& _member: stru->members())
+ ret += _member->type()->canonicalName(false) + " " + _member->name() + ";";
+ ret += "}";
+ }
+ for (auto const& enu: _contractDef.definedEnums())
+ {
+ ret += "enum " + enu->name() + "{";
+ for (ASTPointer<EnumValue> const& val: enu->members())
+ ret += val->name() + ",";
+ if (ret.back() == ',')
+ ret.pop_back();
+ ret += "}";
+ }
+ }
+ if (_contractDef.constructor())
+ {
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
+ solAssert(!!externalFunction, "");
+ ret +=
+ "function " +
+ _contractDef.name() +
+ populateParameters(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
+ ) +
+ ";";
+ }
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ ret += "function " + it.second->declaration().name() +
+ populateParameters(
+ it.second->parameterNames(),
+ it.second->parameterTypeNames(_contractDef.isLibrary())
+ ) + (it.second->isConstant() ? "constant " : "");
+ if (it.second->returnParameterTypes().size())
+ ret += "returns" + populateParameters(
+ it.second->returnParameterNames(),
+ it.second->returnParameterTypeNames(_contractDef.isLibrary())
+ );
+ else if (ret.back() == ' ')
+ ret.pop_back();
+ ret += ";";
+ }
+
+ return ret + "}";
+}
+
+string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
+{
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ if (it.second->hasDeclaration())
+ if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
+ {
+ string value = extractDoc(f->annotation().docTags, "notice");
+ if (!value.empty())
+ {
+ Json::Value user;
+ // since @notice is the only user tag if missing function should not appear
+ user["notice"] = Json::Value(value);
+ methods[it.second->externalSignature()] = user;
+ }
+ }
+ doc["methods"] = methods;
+
+ return Json::StyledWriter().write(doc);
+}
+
+string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
+{
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ auto author = extractDoc(_contractDef.annotation().docTags, "author");
+ if (!author.empty())
+ doc["author"] = author;
+ auto title = extractDoc(_contractDef.annotation().docTags, "title");
+ if (!title.empty())
+ doc["title"] = title;
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ if (!it.second->hasDeclaration())
+ continue;
+ Json::Value method;
+ if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
+ {
+ auto dev = extractDoc(fun->annotation().docTags, "dev");
+ if (!dev.empty())
+ method["details"] = Json::Value(dev);
+
+ auto author = extractDoc(fun->annotation().docTags, "author");
+ if (!author.empty())
+ method["author"] = author;
+
+ auto ret = extractDoc(fun->annotation().docTags, "return");
+ if (!ret.empty())
+ method["return"] = ret;
+
+ Json::Value params(Json::objectValue);
+ auto paramRange = fun->annotation().docTags.equal_range("param");
+ for (auto i = paramRange.first; i != paramRange.second; ++i)
+ params[i->second.paramName] = Json::Value(i->second.content);
+
+ if (!params.empty())
+ method["params"] = params;
+
+ if (!method.empty())
+ // add the function, only if we have any documentation to add
+ methods[it.second->externalSignature()] = method;
+ }
+ }
+ doc["methods"] = methods;
+
+ return Json::StyledWriter().write(doc);
+}
+
+string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
+{
+ string value;
+ auto range = _tags.equal_range(_name);
+ for (auto i = range.first; i != range.second; i++)
+ value += i->second.content;
+ return value;
+}
diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h
new file mode 100644
index 00000000..3e0a1660
--- /dev/null
+++ b/libsolidity/interface/InterfaceHandler.h
@@ -0,0 +1,93 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2014
+ * Takes the parsed AST and produces the Natspec
+ * documentation and the ABI interface
+ * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
+ *
+ * Can generally deal with JSON files
+ */
+
+#pragma once
+
+#include <string>
+#include <memory>
+#include <json/json.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+// Forward declarations
+class ContractDefinition;
+struct DocTag;
+enum class DocumentationType: uint8_t;
+
+enum class DocTagType: uint8_t
+{
+ None = 0,
+ Dev,
+ Notice,
+ Param,
+ Return,
+ Author,
+ Title
+};
+
+enum class CommentOwner
+{
+ Contract,
+ Function
+};
+
+class InterfaceHandler
+{
+public:
+ /// Get the given type of documentation
+ /// @param _contractDef The contract definition
+ /// @param _type The type of the documentation. Can be one of the
+ /// types provided by @c DocumentationType
+ /// @return A string with the json representation of provided type
+ static std::string documentation(
+ ContractDefinition const& _contractDef,
+ DocumentationType _type
+ );
+ /// Get the ABI Interface of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation of the contract's ABI Interface
+ static std::string abiInterface(ContractDefinition const& _contractDef);
+ static std::string ABISolidityInterface(ContractDefinition const& _contractDef);
+ /// Get the User documentation of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation of the contract's user documentation
+ static std::string userDocumentation(ContractDefinition const& _contractDef);
+ /// Genereates the Developer's documentation of the contract
+ /// @param _contractDef The contract definition
+ /// @return A string with the json representation
+ /// of the contract's developer documentation
+ static std::string devDocumentation(ContractDefinition const& _contractDef);
+
+private:
+ /// @returns concatenation of all content under the given tag name.
+ static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name);
+};
+
+} //solidity NS
+} // dev NS
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
new file mode 100644
index 00000000..169e5c18
--- /dev/null
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -0,0 +1,126 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Formatting functions for errors referencing positions and locations in the source.
+ */
+
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/Exceptions.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+void SourceReferenceFormatter::printSourceLocation(
+ ostream& _stream,
+ SourceLocation const& _location,
+ Scanner const& _scanner
+)
+{
+ int startLine;
+ int startColumn;
+ tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
+ int endLine;
+ int endColumn;
+ tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
+ if (startLine == endLine)
+ {
+ string line = _scanner.lineAtPosition(_location.start);
+ _stream << line << endl;
+ for_each(
+ line.cbegin(),
+ line.cbegin() + startColumn,
+ [&_stream](char const& ch) { _stream << (ch == '\t' ? '\t' : ' '); }
+ );
+ _stream << "^";
+ if (endColumn > startColumn + 2)
+ _stream << string(endColumn - startColumn - 2, '-');
+ if (endColumn > startColumn + 1)
+ _stream << "^";
+ _stream << endl;
+ }
+ else
+ _stream <<
+ _scanner.lineAtPosition(_location.start) <<
+ endl <<
+ string(startColumn, ' ') <<
+ "^\n" <<
+ "Spanning multiple lines.\n";
+}
+
+void SourceReferenceFormatter::printSourceName(
+ ostream& _stream,
+ SourceLocation const& _location,
+ Scanner const& _scanner
+)
+{
+ int startLine;
+ int startColumn;
+ tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
+ _stream << *_location.sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
+}
+
+void SourceReferenceFormatter::printExceptionInformation(
+ ostream& _stream,
+ Exception const& _exception,
+ string const& _name,
+ CompilerStack const& _compiler
+)
+{
+ SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
+ auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
+ Scanner const* scannerPtr = nullptr;
+
+ if (location)
+ {
+ scannerPtr = &_compiler.scanner(*location->sourceName);
+ printSourceName(_stream, *location, *scannerPtr);
+ }
+
+ _stream << _name;
+ if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
+ _stream << ": " << *description << endl;
+
+ if (location)
+ {
+ scannerPtr = &_compiler.scanner(*location->sourceName);
+ printSourceLocation(_stream, *location, *scannerPtr);
+ }
+
+ if (secondarylocation && !secondarylocation->infos.empty())
+ {
+ for (auto info: secondarylocation->infos)
+ {
+ scannerPtr = &_compiler.scanner(*info.second.sourceName);
+ _stream << info.first << " ";
+ printSourceName(_stream, info.second, *scannerPtr);
+ _stream << endl;
+ printSourceLocation(_stream, info.second, *scannerPtr);
+ }
+ _stream << endl;
+ }
+}
+
+}
+}
diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h
new file mode 100644
index 00000000..dd258c27
--- /dev/null
+++ b/libsolidity/interface/SourceReferenceFormatter.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Formatting functions for errors referencing positions and locations in the source.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <libevmasm/SourceLocation.h>
+
+namespace dev
+{
+
+struct Exception; // forward
+
+namespace solidity
+{
+
+class Scanner; // forward
+class CompilerStack; // forward
+
+struct SourceReferenceFormatter
+{
+public:
+ static void printSourceLocation(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner);
+ static void printExceptionInformation(
+ std::ostream& _stream,
+ Exception const& _exception,
+ std::string const& _name,
+ CompilerStack const& _compiler
+ );
+private:
+ static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner);
+};
+
+}
+}
diff --git a/libsolidity/interface/Utils.h b/libsolidity/interface/Utils.h
new file mode 100644
index 00000000..738669ac
--- /dev/null
+++ b/libsolidity/interface/Utils.h
@@ -0,0 +1,39 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity Utilities.
+ */
+
+#pragma once
+
+#include <libdevcore/Assertions.h>
+#include <libsolidity/interface/Exceptions.h>
+
+namespace dev
+{
+namespace solidity
+{
+struct InternalCompilerError;
+}
+}
+
+/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
+#define solAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
+
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
new file mode 100644
index 00000000..84a82dbf
--- /dev/null
+++ b/libsolidity/interface/Version.cpp
@@ -0,0 +1,76 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Versioning.
+ */
+
+#include <libsolidity/interface/Version.h>
+#include <string>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Common.h>
+#include <libevmasm/Version.h>
+#include <libsolidity/interface/Utils.h>
+#include <solidity/BuildInfo.h>
+
+using namespace dev;
+using namespace dev::solidity;
+using namespace std;
+
+char const* dev::solidity::VersionNumber = ETH_PROJECT_VERSION;
+
+string const dev::solidity::VersionString =
+ string(dev::solidity::VersionNumber) +
+ "-" +
+ string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) +
+ (ETH_CLEAN_REPO ? "" : "*") +
+ "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM)
+ " linked to libethereum-" + eth::VersionStringLibEvmAsm;
+
+
+bytes dev::solidity::binaryVersion()
+{
+ bytes ret{0};
+ size_t i = 0;
+ auto parseDecimal = [&]()
+ {
+ size_t ret = 0;
+ solAssert('0' <= VersionString[i] && VersionString[i] <= '9', "");
+ for (; i < VersionString.size() && '0' <= VersionString[i] && VersionString[i] <= '9'; ++i)
+ ret = ret * 10 + (VersionString[i] - '0');
+ return ret;
+ };
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '-', "");
+ ++i;
+ solAssert(i + 7 < VersionString.size(), "");
+ bytes commitHash = fromHex(VersionString.substr(i, 8));
+ if (commitHash.empty())
+ commitHash = bytes(4, 0);
+ ret += commitHash;
+ solAssert(ret.size() == 1 + 3 + 4, "");
+
+ return ret;
+}
+
diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h
new file mode 100644
index 00000000..fea73997
--- /dev/null
+++ b/libsolidity/interface/Version.h
@@ -0,0 +1,42 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Versioning.
+ */
+
+#pragma once
+
+#include <string>
+#include <libdevcore/Common.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+extern char const* VersionNumber;
+extern std::string const VersionString;
+
+/// @returns a binary form of the version string, where A.B.C-HASH is encoded such that
+/// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals)
+/// and HASH is interpreted as 8 hex digits and encoded into the last four bytes.
+bytes binaryVersion();
+
+}
+}
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
new file mode 100644
index 00000000..bbee35f5
--- /dev/null
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -0,0 +1,141 @@
+
+#include <libsolidity/parsing/DocStringParser.h>
+#include <boost/range/irange.hpp>
+#include <libsolidity/interface/Utils.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+static inline string::const_iterator skipLineOrEOS(
+ string::const_iterator _nlPos,
+ string::const_iterator _end
+)
+{
+ return (_nlPos == _end) ? _end : ++_nlPos;
+}
+
+static inline string::const_iterator firstSpaceOrNl(
+ string::const_iterator _pos,
+ string::const_iterator _end
+)
+{
+ auto spacePos = find(_pos, _end, ' ');
+ auto nlPos = find(_pos, _end, '\n');
+ return (spacePos < nlPos) ? spacePos : nlPos;
+}
+
+bool DocStringParser::parse(string const& _docString, ErrorList& _errors)
+{
+ m_errors = &_errors;
+ m_errorsOccurred = false;
+ m_lastTag = nullptr;
+
+ auto currPos = _docString.begin();
+ auto end = _docString.end();
+
+ while (currPos != end)
+ {
+ auto tagPos = find(currPos, end, '@');
+ auto nlPos = find(currPos, end, '\n');
+
+ if (tagPos != end && tagPos < nlPos)
+ {
+ // we found a tag
+ auto tagNameEndPos = firstSpaceOrNl(tagPos, end);
+ if (tagNameEndPos == end)
+ {
+ appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found");
+ break;
+ }
+
+ currPos = parseDocTag(tagNameEndPos + 1, end, string(tagPos + 1, tagNameEndPos));
+ }
+ else if (!!m_lastTag) // continuation of the previous tag
+ currPos = appendDocTag(currPos, end);
+ else if (currPos != end)
+ {
+ // if it begins without a tag then consider it as @notice
+ if (currPos == _docString.begin())
+ {
+ currPos = parseDocTag(currPos, end, "notice");
+ continue;
+ }
+ else if (nlPos == end) //end of text
+ break;
+ // else skip the line if a newline was found and we get here
+ currPos = nlPos + 1;
+ }
+ }
+ return !m_errorsOccurred;
+}
+
+DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, bool _appending)
+{
+ solAssert(!!m_lastTag, "");
+ auto nlPos = find(_pos, _end, '\n');
+ if (_appending && _pos < _end && *_pos != ' ')
+ m_lastTag->content += " ";
+ copy(_pos, nlPos, back_inserter(m_lastTag->content));
+ return skipLineOrEOS(nlPos, _end);
+}
+
+DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
+{
+ // find param name
+ auto currPos = find(_pos, _end, ' ');
+ if (currPos == _end)
+ {
+ appendError("End of param name not found" + string(_pos, _end));
+ return _end;
+ }
+
+ auto paramName = string(_pos, currPos);
+
+ currPos += 1;
+ auto nlPos = find(currPos, _end, '\n');
+ auto paramDesc = string(currPos, nlPos);
+ newTag("param");
+ m_lastTag->paramName = paramName;
+ m_lastTag->content = paramDesc;
+
+ return skipLineOrEOS(nlPos, _end);
+}
+
+DocStringParser::iter DocStringParser::parseDocTag(iter _pos, iter _end, string const& _tag)
+{
+ // LTODO: need to check for @(start of a tag) between here and the end of line
+ // for all cases.
+ if (!m_lastTag || _tag != "")
+ {
+ if (_tag == "param")
+ return parseDocTagParam(_pos, _end);
+ else
+ {
+ newTag(_tag);
+ return parseDocTagLine(_pos, _end, false);
+ }
+ }
+ else
+ return appendDocTag(_pos, _end);
+}
+
+DocStringParser::iter DocStringParser::appendDocTag(iter _pos, iter _end)
+{
+ solAssert(!!m_lastTag, "");
+ return parseDocTagLine(_pos, _end, true);
+}
+
+void DocStringParser::newTag(string const& _tagName)
+{
+ m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second;
+}
+
+void DocStringParser::appendError(string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DocstringParsingError);
+ *err << errinfo_comment(_description);
+ m_errors->push_back(err);
+ m_errorsOccurred = true;
+}
diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h
new file mode 100644
index 00000000..f67b8bbd
--- /dev/null
+++ b/libsolidity/parsing/DocStringParser.h
@@ -0,0 +1,70 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2014, 2015
+ * Parses a given docstring into pieces introduced by tags.
+ */
+
+#pragma once
+
+#include <string>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class DocStringParser
+{
+public:
+ /// Parse the given @a _docString and stores the parsed components internally.
+ /// @returns false on error and appends the error to @a _errors.
+ bool parse(std::string const& _docString, ErrorList& _errors);
+
+ std::multimap<std::string, DocTag> const& tags() const { return m_docTags; }
+
+private:
+ using iter = std::string::const_iterator;
+ void resetUser();
+ void resetDev();
+
+ iter parseDocTagLine(iter _pos, iter _end, bool _appending);
+ iter parseDocTagParam(iter _pos, iter _end);
+ iter appendDocTagParam(iter _pos, iter _end);
+ void parseDocString(std::string const& _string);
+ iter appendDocTag(iter _pos, iter _end);
+ /// Parses the doc tag named @a _tag, adds it to m_docTags and returns the position
+ /// after the tag.
+ iter parseDocTag(iter _pos, iter _end, std::string const& _tag);
+
+ /// Creates and inserts a new tag and adjusts m_lastTag.
+ void newTag(std::string const& _tagName);
+
+ void appendError(std::string const& _description);
+
+ /// Mapping tag name -> content.
+ std::multimap<std::string, DocTag> m_docTags;
+ DocTag* m_lastTag = nullptr;
+ ErrorList* m_errors = nullptr;
+ bool m_errorsOccurred = false;
+};
+
+} //solidity NS
+} // dev NS
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
new file mode 100644
index 00000000..7bda3610
--- /dev/null
+++ b/libsolidity/parsing/Parser.cpp
@@ -0,0 +1,1282 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity parser.
+ */
+
+#include <vector>
+#include <libdevcore/Log.h>
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/InterfaceHandler.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+/// AST node factory that also tracks the begin and end position of an AST node
+/// while it is being parsed
+class Parser::ASTNodeFactory
+{
+public:
+ ASTNodeFactory(Parser const& _parser):
+ m_parser(_parser), m_location(_parser.position(), -1, _parser.sourceName()) {}
+ ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
+ m_parser(_parser), m_location(_childNode->location()) {}
+
+ void markEndPosition() { m_location.end = m_parser.endPosition(); }
+ void setLocation(SourceLocation const& _location) { m_location = _location; }
+ void setLocationEmpty() { m_location.end = m_location.start; }
+ /// Set the end position to the one of the given node.
+ void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->location().end; }
+
+ template <class NodeType, typename... Args>
+ ASTPointer<NodeType> createNode(Args&& ... _args)
+ {
+ if (m_location.end < 0)
+ markEndPosition();
+ return make_shared<NodeType>(m_location, forward<Args>(_args)...);
+ }
+
+private:
+ Parser const& m_parser;
+ SourceLocation m_location;
+};
+
+ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
+{
+ try
+ {
+ m_scanner = _scanner;
+ ASTNodeFactory nodeFactory(*this);
+ vector<ASTPointer<ASTNode>> nodes;
+ while (m_scanner->currentToken() != Token::EOS)
+ {
+ switch (auto token = m_scanner->currentToken())
+ {
+ case Token::Import:
+ nodes.push_back(parseImportDirective());
+ break;
+ case Token::Contract:
+ case Token::Library:
+ nodes.push_back(parseContractDefinition(token == Token::Library));
+ break;
+ default:
+ fatalParserError(string("Expected import directive or contract definition."));
+ }
+ }
+ return nodeFactory.createNode<SourceUnit>(nodes);
+ }
+ catch (FatalError const&)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return nullptr;
+ }
+}
+
+ASTPointer<ImportDirective> Parser::parseImportDirective()
+{
+ // import "abc" [as x];
+ // import * as x from "abc";
+ // import {a as b, c} from "abc";
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::Import);
+ ASTPointer<ASTString> path;
+ ASTPointer<ASTString> unitAlias = make_shared<string>();
+ vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases;
+
+ if (m_scanner->currentToken() == Token::StringLiteral)
+ {
+ path = getLiteralAndAdvance();
+ if (m_scanner->currentToken() == Token::As)
+ {
+ m_scanner->next();
+ unitAlias = expectIdentifierToken();
+ }
+ }
+ else
+ {
+ if (m_scanner->currentToken() == Token::LBrace)
+ {
+ m_scanner->next();
+ while (true)
+ {
+ ASTPointer<Identifier> id = parseIdentifier();
+ ASTPointer<ASTString> alias;
+ if (m_scanner->currentToken() == Token::As)
+ {
+ expectToken(Token::As);
+ alias = expectIdentifierToken();
+ }
+ symbolAliases.push_back(make_pair(move(id), move(alias)));
+ if (m_scanner->currentToken() != Token::Comma)
+ break;
+ m_scanner->next();
+ }
+ expectToken(Token::RBrace);
+ }
+ else if (m_scanner->currentToken() == Token::Mul)
+ {
+ m_scanner->next();
+ expectToken(Token::As);
+ unitAlias = expectIdentifierToken();
+ }
+ else
+ fatalParserError("Expected string literal (path), \"*\" or alias list.");
+ // "from" is not a keyword but parsed as an identifier because of backwards
+ // compatibility and because it is a really common word.
+ if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
+ fatalParserError("Expected \"from\".");
+ m_scanner->next();
+ if (m_scanner->currentToken() != Token::StringLiteral)
+ fatalParserError("Expected import path.");
+ path = getLiteralAndAdvance();
+ }
+ nodeFactory.markEndPosition();
+ expectToken(Token::Semicolon);
+ return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
+}
+
+ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docString;
+ if (m_scanner->currentCommentLiteral() != "")
+ docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
+ expectToken(_isLibrary ? Token::Library : Token::Contract);
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ vector<ASTPointer<InheritanceSpecifier>> baseContracts;
+ if (m_scanner->currentToken() == Token::Is)
+ do
+ {
+ m_scanner->next();
+ baseContracts.push_back(parseInheritanceSpecifier());
+ }
+ while (m_scanner->currentToken() == Token::Comma);
+ vector<ASTPointer<ASTNode>> subNodes;
+ expectToken(Token::LBrace);
+ while (true)
+ {
+ Token::Value currentTokenValue = m_scanner->currentToken();
+ if (currentTokenValue == Token::RBrace)
+ break;
+ else if (currentTokenValue == Token::Function)
+ subNodes.push_back(parseFunctionDefinition(name.get()));
+ else if (currentTokenValue == Token::Struct)
+ subNodes.push_back(parseStructDefinition());
+ else if (currentTokenValue == Token::Enum)
+ subNodes.push_back(parseEnumDefinition());
+ else if (
+ currentTokenValue == Token::Identifier ||
+ currentTokenValue == Token::Mapping ||
+ Token::isElementaryTypeName(currentTokenValue)
+ )
+ {
+ VarDeclParserOptions options;
+ options.isStateVariable = true;
+ options.allowInitialValue = true;
+ subNodes.push_back(parseVariableDeclaration(options));
+ expectToken(Token::Semicolon);
+ }
+ else if (currentTokenValue == Token::Modifier)
+ subNodes.push_back(parseModifierDefinition());
+ else if (currentTokenValue == Token::Event)
+ subNodes.push_back(parseEventDefinition());
+ else if (currentTokenValue == Token::Using)
+ subNodes.push_back(parseUsingDirective());
+ else
+ fatalParserError(string("Function, variable, struct or modifier declaration expected."));
+ }
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<ContractDefinition>(
+ name,
+ docString,
+ baseContracts,
+ subNodes,
+ _isLibrary
+ );
+}
+
+ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName());
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->currentToken() == Token::LParen)
+ {
+ m_scanner->next();
+ arguments = parseFunctionCallListArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(name);
+ return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
+}
+
+Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
+{
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ if (_token == Token::Public)
+ visibility = Declaration::Visibility::Public;
+ else if (_token == Token::Internal)
+ visibility = Declaration::Visibility::Internal;
+ else if (_token == Token::Private)
+ visibility = Declaration::Visibility::Private;
+ else if (_token == Token::External)
+ visibility = Declaration::Visibility::External;
+ else
+ solAssert(false, "Invalid visibility specifier.");
+ m_scanner->next();
+ return visibility;
+}
+
+ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docstring;
+ if (m_scanner->currentCommentLiteral() != "")
+ docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
+
+ expectToken(Token::Function);
+ ASTPointer<ASTString> name;
+ if (m_scanner->currentToken() == Token::LParen)
+ name = make_shared<ASTString>(); // anonymous function
+ else
+ name = expectIdentifierToken();
+ VarDeclParserOptions options;
+ options.allowLocationSpecifier = true;
+ ASTPointer<ParameterList> parameters(parseParameterList(options));
+ bool isDeclaredConst = false;
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ vector<ASTPointer<ModifierInvocation>> modifiers;
+ while (true)
+ {
+ Token::Value token = m_scanner->currentToken();
+ if (token == Token::Const)
+ {
+ isDeclaredConst = true;
+ m_scanner->next();
+ }
+ else if (token == Token::Identifier)
+ modifiers.push_back(parseModifierInvocation());
+ else if (Token::isVisibilitySpecifier(token))
+ {
+ if (visibility != Declaration::Visibility::Default)
+ fatalParserError(string("Multiple visibility specifiers."));
+ visibility = parseVisibilitySpecifier(token);
+ }
+ else
+ break;
+ }
+ ASTPointer<ParameterList> returnParameters;
+ if (m_scanner->currentToken() == Token::Returns)
+ {
+ bool const permitEmptyParameterList = false;
+ m_scanner->next();
+ returnParameters = parseParameterList(options, permitEmptyParameterList);
+ }
+ else
+ returnParameters = createEmptyParameterList();
+ ASTPointer<Block> block = ASTPointer<Block>();
+ nodeFactory.markEndPosition();
+ if (m_scanner->currentToken() != Token::Semicolon)
+ {
+ block = parseBlock();
+ nodeFactory.setEndPositionFromNode(block);
+ }
+ else
+ m_scanner->next(); // just consume the ';'
+ bool const c_isConstructor = (_contractName && *name == *_contractName);
+ return nodeFactory.createNode<FunctionDefinition>(
+ name,
+ visibility,
+ c_isConstructor,
+ docstring,
+ parameters,
+ isDeclaredConst,
+ modifiers,
+ returnParameters,
+ block
+ );
+}
+
+ASTPointer<StructDefinition> Parser::parseStructDefinition()
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::Struct);
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ vector<ASTPointer<VariableDeclaration>> members;
+ expectToken(Token::LBrace);
+ while (m_scanner->currentToken() != Token::RBrace)
+ {
+ members.push_back(parseVariableDeclaration());
+ expectToken(Token::Semicolon);
+ }
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<StructDefinition>(name, members);
+}
+
+ASTPointer<EnumValue> Parser::parseEnumValue()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.markEndPosition();
+ return nodeFactory.createNode<EnumValue>(expectIdentifierToken());
+}
+
+ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::Enum);
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ vector<ASTPointer<EnumValue>> members;
+ expectToken(Token::LBrace);
+
+ while (m_scanner->currentToken() != Token::RBrace)
+ {
+ members.push_back(parseEnumValue());
+ if (m_scanner->currentToken() == Token::RBrace)
+ break;
+ expectToken(Token::Comma);
+ if (m_scanner->currentToken() != Token::Identifier)
+ fatalParserError(string("Expected Identifier after ','"));
+ }
+
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<EnumDefinition>(name, members);
+}
+
+ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
+ VarDeclParserOptions const& _options,
+ ASTPointer<TypeName> const& _lookAheadArrayType
+)
+{
+ ASTNodeFactory nodeFactory = _lookAheadArrayType ?
+ ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
+ ASTPointer<TypeName> type;
+ if (_lookAheadArrayType)
+ type = _lookAheadArrayType;
+ else
+ {
+ type = parseTypeName(_options.allowVar);
+ if (type != nullptr)
+ nodeFactory.setEndPositionFromNode(type);
+ }
+ bool isIndexed = false;
+ bool isDeclaredConst = false;
+ Declaration::Visibility visibility(Declaration::Visibility::Default);
+ VariableDeclaration::Location location = VariableDeclaration::Location::Default;
+ ASTPointer<ASTString> identifier;
+
+ while (true)
+ {
+ Token::Value token = m_scanner->currentToken();
+ if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
+ {
+ if (visibility != Declaration::Visibility::Default)
+ fatalParserError(string("Visibility already specified."));
+ visibility = parseVisibilitySpecifier(token);
+ }
+ else
+ {
+ if (_options.allowIndexed && token == Token::Indexed)
+ isIndexed = true;
+ else if (token == Token::Const)
+ isDeclaredConst = true;
+ else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
+ {
+ if (location != VariableDeclaration::Location::Default)
+ fatalParserError(string("Location already specified."));
+ if (!type)
+ fatalParserError(string("Location specifier needs explicit type name."));
+ location = (
+ token == Token::Memory ?
+ VariableDeclaration::Location::Memory :
+ VariableDeclaration::Location::Storage
+ );
+ }
+ else
+ break;
+ m_scanner->next();
+ }
+ }
+ nodeFactory.markEndPosition();
+
+ if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier)
+ {
+ identifier = make_shared<ASTString>("");
+ solAssert(type != nullptr, "");
+ nodeFactory.setEndPositionFromNode(type);
+ }
+ else
+ identifier = expectIdentifierToken();
+ ASTPointer<Expression> value;
+ if (_options.allowInitialValue)
+ {
+ if (m_scanner->currentToken() == Token::Assign)
+ {
+ m_scanner->next();
+ value = parseExpression();
+ nodeFactory.setEndPositionFromNode(value);
+ }
+ }
+ return nodeFactory.createNode<VariableDeclaration>(
+ type,
+ identifier,
+ value,
+ visibility,
+ _options.isStateVariable,
+ isIndexed,
+ isDeclaredConst,
+ location
+ );
+}
+
+ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
+{
+ ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
+ m_insideModifier = true;
+
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docstring;
+ if (m_scanner->currentCommentLiteral() != "")
+ docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
+
+ expectToken(Token::Modifier);
+ ASTPointer<ASTString> name(expectIdentifierToken());
+ ASTPointer<ParameterList> parameters;
+ if (m_scanner->currentToken() == Token::LParen)
+ {
+ VarDeclParserOptions options;
+ options.allowIndexed = true;
+ options.allowLocationSpecifier = true;
+ parameters = parseParameterList(options);
+ }
+ else
+ parameters = createEmptyParameterList();
+ ASTPointer<Block> block = parseBlock();
+ nodeFactory.setEndPositionFromNode(block);
+ return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
+}
+
+ASTPointer<EventDefinition> Parser::parseEventDefinition()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<ASTString> docstring;
+ if (m_scanner->currentCommentLiteral() != "")
+ docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
+
+ expectToken(Token::Event);
+ ASTPointer<ASTString> name(expectIdentifierToken());
+ ASTPointer<ParameterList> parameters;
+ if (m_scanner->currentToken() == Token::LParen)
+ {
+ VarDeclParserOptions options;
+ options.allowIndexed = true;
+ parameters = parseParameterList(options);
+ }
+ else
+ parameters = createEmptyParameterList();
+ bool anonymous = false;
+ if (m_scanner->currentToken() == Token::Anonymous)
+ {
+ anonymous = true;
+ m_scanner->next();
+ }
+ nodeFactory.markEndPosition();
+ expectToken(Token::Semicolon);
+ return nodeFactory.createNode<EventDefinition>(name, docstring, parameters, anonymous);
+}
+
+ASTPointer<UsingForDirective> Parser::parseUsingDirective()
+{
+ ASTNodeFactory nodeFactory(*this);
+
+ expectToken(Token::Using);
+ ASTPointer<UserDefinedTypeName> library(parseUserDefinedTypeName());
+ ASTPointer<TypeName> typeName;
+ expectToken(Token::For);
+ if (m_scanner->currentToken() == Token::Mul)
+ m_scanner->next();
+ else
+ typeName = parseTypeName(false);
+ nodeFactory.markEndPosition();
+ expectToken(Token::Semicolon);
+ return nodeFactory.createNode<UsingForDirective>(library, typeName);
+}
+
+ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Identifier> name(parseIdentifier());
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->currentToken() == Token::LParen)
+ {
+ m_scanner->next();
+ arguments = parseFunctionCallListArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(name);
+ return nodeFactory.createNode<ModifierInvocation>(name, arguments);
+}
+
+ASTPointer<Identifier> Parser::parseIdentifier()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.markEndPosition();
+ return nodeFactory.createNode<Identifier>(expectIdentifierToken());
+}
+
+ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.markEndPosition();
+ vector<ASTString> identifierPath{*expectIdentifierToken()};
+ while (m_scanner->currentToken() == Token::Period)
+ {
+ m_scanner->next();
+ nodeFactory.markEndPosition();
+ identifierPath.push_back(*expectIdentifierToken());
+ }
+ return nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
+}
+
+ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<TypeName> type;
+ Token::Value token = m_scanner->currentToken();
+ if (Token::isElementaryTypeName(token))
+ {
+ unsigned firstSize;
+ unsigned secondSize;
+ tie(firstSize, secondSize) = m_scanner->currentTokenInfo();
+ ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize);
+ type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName);
+ m_scanner->next();
+ }
+ else if (token == Token::Var)
+ {
+ if (!_allowVar)
+ fatalParserError(string("Expected explicit type name."));
+ m_scanner->next();
+ }
+ else if (token == Token::Mapping)
+ type = parseMapping();
+ else if (token == Token::Identifier)
+ type = parseUserDefinedTypeName();
+ else
+ fatalParserError(string("Expected type name"));
+
+ if (type)
+ // Parse "[...]" postfixes for arrays.
+ while (m_scanner->currentToken() == Token::LBrack)
+ {
+ m_scanner->next();
+ ASTPointer<Expression> length;
+ if (m_scanner->currentToken() != Token::RBrack)
+ length = parseExpression();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrack);
+ type = nodeFactory.createNode<ArrayTypeName>(type, length);
+ }
+ return type;
+}
+
+ASTPointer<Mapping> Parser::parseMapping()
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::Mapping);
+ expectToken(Token::LParen);
+ ASTPointer<ElementaryTypeName> keyType;
+ Token::Value token = m_scanner->currentToken();
+ if (!Token::isElementaryTypeName(token))
+ fatalParserError(string("Expected elementary type name for mapping key type"));
+ unsigned firstSize;
+ unsigned secondSize;
+ tie(firstSize, secondSize) = m_scanner->currentTokenInfo();
+ ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize);
+ keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName);
+ m_scanner->next();
+ expectToken(Token::Arrow);
+ bool const allowVar = false;
+ ASTPointer<TypeName> valueType = parseTypeName(allowVar);
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ return nodeFactory.createNode<Mapping>(keyType, valueType);
+}
+
+ASTPointer<ParameterList> Parser::parseParameterList(
+ VarDeclParserOptions const& _options,
+ bool _allowEmpty
+)
+{
+ ASTNodeFactory nodeFactory(*this);
+ vector<ASTPointer<VariableDeclaration>> parameters;
+ VarDeclParserOptions options(_options);
+ options.allowEmptyName = true;
+ expectToken(Token::LParen);
+ if (!_allowEmpty || m_scanner->currentToken() != Token::RParen)
+ {
+ parameters.push_back(parseVariableDeclaration(options));
+ while (m_scanner->currentToken() != Token::RParen)
+ {
+ expectToken(Token::Comma);
+ parameters.push_back(parseVariableDeclaration(options));
+ }
+ }
+ nodeFactory.markEndPosition();
+ m_scanner->next();
+ return nodeFactory.createNode<ParameterList>(parameters);
+}
+
+ASTPointer<Block> Parser::parseBlock(ASTPointer<ASTString> const& _docString)
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::LBrace);
+ vector<ASTPointer<Statement>> statements;
+ while (m_scanner->currentToken() != Token::RBrace)
+ statements.push_back(parseStatement());
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrace);
+ return nodeFactory.createNode<Block>(_docString, statements);
+}
+
+ASTPointer<Statement> Parser::parseStatement()
+{
+ ASTPointer<ASTString> docString;
+ if (m_scanner->currentCommentLiteral() != "")
+ docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
+ ASTPointer<Statement> statement;
+ switch (m_scanner->currentToken())
+ {
+ case Token::If:
+ return parseIfStatement(docString);
+ case Token::While:
+ return parseWhileStatement(docString);
+ case Token::For:
+ return parseForStatement(docString);
+ case Token::LBrace:
+ return parseBlock(docString);
+ // starting from here, all statements must be terminated by a semicolon
+ case Token::Continue:
+ statement = ASTNodeFactory(*this).createNode<Continue>(docString);
+ m_scanner->next();
+ break;
+ case Token::Break:
+ statement = ASTNodeFactory(*this).createNode<Break>(docString);
+ m_scanner->next();
+ break;
+ case Token::Return:
+ {
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Expression> expression;
+ if (m_scanner->next() != Token::Semicolon)
+ {
+ expression = parseExpression();
+ nodeFactory.setEndPositionFromNode(expression);
+ }
+ statement = nodeFactory.createNode<Return>(docString, expression);
+ break;
+ }
+ case Token::Throw:
+ {
+ statement = ASTNodeFactory(*this).createNode<Throw>(docString);
+ m_scanner->next();
+ break;
+ }
+ case Token::Identifier:
+ if (m_insideModifier && m_scanner->currentLiteral() == "_")
+ {
+ statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
+ m_scanner->next();
+ return statement;
+ }
+ // fall-through
+ default:
+ statement = parseSimpleStatement(docString);
+ }
+ expectToken(Token::Semicolon);
+ return statement;
+}
+
+ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::If);
+ expectToken(Token::LParen);
+ ASTPointer<Expression> condition = parseExpression();
+ expectToken(Token::RParen);
+ ASTPointer<Statement> trueBody = parseStatement();
+ ASTPointer<Statement> falseBody;
+ if (m_scanner->currentToken() == Token::Else)
+ {
+ m_scanner->next();
+ falseBody = parseStatement();
+ nodeFactory.setEndPositionFromNode(falseBody);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(trueBody);
+ return nodeFactory.createNode<IfStatement>(_docString, condition, trueBody, falseBody);
+}
+
+ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> const& _docString)
+{
+ ASTNodeFactory nodeFactory(*this);
+ expectToken(Token::While);
+ expectToken(Token::LParen);
+ ASTPointer<Expression> condition = parseExpression();
+ expectToken(Token::RParen);
+ ASTPointer<Statement> body = parseStatement();
+ nodeFactory.setEndPositionFromNode(body);
+ return nodeFactory.createNode<WhileStatement>(_docString, condition, body);
+}
+
+ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Statement> initExpression;
+ ASTPointer<Expression> conditionExpression;
+ ASTPointer<ExpressionStatement> loopExpression;
+ expectToken(Token::For);
+ expectToken(Token::LParen);
+
+ // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
+ if (m_scanner->currentToken() != Token::Semicolon)
+ initExpression = parseSimpleStatement(ASTPointer<ASTString>());
+ expectToken(Token::Semicolon);
+
+ if (m_scanner->currentToken() != Token::Semicolon)
+ conditionExpression = parseExpression();
+ expectToken(Token::Semicolon);
+
+ if (m_scanner->currentToken() != Token::RParen)
+ loopExpression = parseExpressionStatement(ASTPointer<ASTString>());
+ expectToken(Token::RParen);
+
+ ASTPointer<Statement> body = parseStatement();
+ nodeFactory.setEndPositionFromNode(body);
+ return nodeFactory.createNode<ForStatement>(
+ _docString,
+ initExpression,
+ conditionExpression,
+ loopExpression,
+ body
+ );
+}
+
+ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
+{
+ // These two cases are very hard to distinguish:
+ // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
+ // In the first case, x is a type name, in the second it is the name of a variable.
+ // As an extension, we can even have:
+ // `x.y.z[1][2] a;` and `x.y.z[1][2] = 10;`
+ // Where in the first, x.y.z leads to a type name where in the second, it accesses structs.
+ switch (peekStatementType())
+ {
+ case LookAheadInfo::VariableDeclarationStatement:
+ return parseVariableDeclarationStatement(_docString);
+ case LookAheadInfo::ExpressionStatement:
+ return parseExpressionStatement(_docString);
+ default:
+ break;
+ }
+ // At this point, we have 'Identifier "["' or 'Identifier "." Identifier' or 'ElementoryTypeName "["'.
+ // We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )+'
+ // until we can decide whether to hand this over to ExpressionStatement or create a
+ // VariableDeclarationStatement out of it.
+ vector<ASTPointer<PrimaryExpression>> path;
+ bool startedWithElementary = false;
+ if (m_scanner->currentToken() == Token::Identifier)
+ path.push_back(parseIdentifier());
+ else
+ {
+ startedWithElementary = true;
+ unsigned firstNum;
+ unsigned secondNum;
+ tie(firstNum, secondNum) = m_scanner->currentTokenInfo();
+ ElementaryTypeNameToken elemToken(m_scanner->currentToken(), firstNum, secondNum);
+ path.push_back(ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(elemToken));
+ m_scanner->next();
+ }
+ while (!startedWithElementary && m_scanner->currentToken() == Token::Period)
+ {
+ m_scanner->next();
+ path.push_back(parseIdentifier());
+ }
+ vector<pair<ASTPointer<Expression>, SourceLocation>> indices;
+ while (m_scanner->currentToken() == Token::LBrack)
+ {
+ expectToken(Token::LBrack);
+ ASTPointer<Expression> index;
+ if (m_scanner->currentToken() != Token::RBrack)
+ index = parseExpression();
+ SourceLocation indexLocation = path.front()->location();
+ indexLocation.end = endPosition();
+ indices.push_back(make_pair(index, indexLocation));
+ expectToken(Token::RBrack);
+ }
+
+ if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken()))
+ return parseVariableDeclarationStatement(_docString, typeNameIndexAccessStructure(path, indices));
+ else
+ return parseExpressionStatement(_docString, expressionFromIndexAccessStructure(path, indices));
+}
+
+ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<TypeName> const& _lookAheadArrayType
+)
+{
+ ASTNodeFactory nodeFactory(*this);
+ if (_lookAheadArrayType)
+ nodeFactory.setLocation(_lookAheadArrayType->location());
+ vector<ASTPointer<VariableDeclaration>> variables;
+ ASTPointer<Expression> value;
+ if (
+ !_lookAheadArrayType &&
+ m_scanner->currentToken() == Token::Var &&
+ m_scanner->peekNextToken() == Token::LParen
+ )
+ {
+ // Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables.
+ m_scanner->next();
+ m_scanner->next();
+ if (m_scanner->currentToken() != Token::RParen)
+ while (true)
+ {
+ ASTPointer<VariableDeclaration> var;
+ if (
+ m_scanner->currentToken() != Token::Comma &&
+ m_scanner->currentToken() != Token::RParen
+ )
+ {
+ ASTNodeFactory varDeclNodeFactory(*this);
+ varDeclNodeFactory.markEndPosition();
+ ASTPointer<ASTString> name = expectIdentifierToken();
+ var = varDeclNodeFactory.createNode<VariableDeclaration>(
+ ASTPointer<TypeName>(),
+ name,
+ ASTPointer<Expression>(),
+ VariableDeclaration::Visibility::Default
+ );
+ }
+ variables.push_back(var);
+ if (m_scanner->currentToken() == Token::RParen)
+ break;
+ else
+ expectToken(Token::Comma);
+ }
+ nodeFactory.markEndPosition();
+ m_scanner->next();
+ }
+ else
+ {
+ VarDeclParserOptions options;
+ options.allowVar = true;
+ options.allowLocationSpecifier = true;
+ variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType));
+ }
+ if (m_scanner->currentToken() == Token::Assign)
+ {
+ m_scanner->next();
+ value = parseExpression();
+ nodeFactory.setEndPositionFromNode(value);
+ }
+ return nodeFactory.createNode<VariableDeclarationStatement>(_docString, variables, value);
+}
+
+ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+)
+{
+ ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
+ return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(_docString, expression);
+}
+
+ASTPointer<Expression> Parser::parseExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+)
+{
+ ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
+ if (Token::isAssignmentOp(m_scanner->currentToken()))
+ {
+ Token::Value assignmentOperator = expectAssignmentOperator();
+ ASTPointer<Expression> rightHandSide = parseExpression();
+ ASTNodeFactory nodeFactory(*this, expression);
+ nodeFactory.setEndPositionFromNode(rightHandSide);
+ return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
+ }
+ else if (m_scanner->currentToken() == Token::Value::Conditional)
+ {
+ m_scanner->next();
+ ASTPointer<Expression> trueExpression = parseExpression();
+ expectToken(Token::Colon);
+ ASTPointer<Expression> falseExpression = parseExpression();
+ ASTNodeFactory nodeFactory(*this, expression);
+ nodeFactory.setEndPositionFromNode(falseExpression);
+ return nodeFactory.createNode<Conditional>(expression, trueExpression, falseExpression);
+ }
+ else
+ return expression;
+}
+
+ASTPointer<Expression> Parser::parseBinaryExpression(
+ int _minPrecedence,
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+)
+{
+ ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
+ ASTNodeFactory nodeFactory(*this, expression);
+ int precedence = Token::precedence(m_scanner->currentToken());
+ for (; precedence >= _minPrecedence; --precedence)
+ while (Token::precedence(m_scanner->currentToken()) == precedence)
+ {
+ Token::Value op = m_scanner->currentToken();
+ m_scanner->next();
+ ASTPointer<Expression> right = parseBinaryExpression(precedence + 1);
+ nodeFactory.setEndPositionFromNode(right);
+ expression = nodeFactory.createNode<BinaryOperation>(expression, op, right);
+ }
+ return expression;
+}
+
+ASTPointer<Expression> Parser::parseUnaryExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+)
+{
+ ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
+ ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
+ Token::Value token = m_scanner->currentToken();
+ if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
+ {
+ // prefix expression
+ m_scanner->next();
+ ASTPointer<Expression> subExpression = parseUnaryExpression();
+ nodeFactory.setEndPositionFromNode(subExpression);
+ return nodeFactory.createNode<UnaryOperation>(token, subExpression, true);
+ }
+ else
+ {
+ // potential postfix expression
+ ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
+ token = m_scanner->currentToken();
+ if (!Token::isCountOp(token))
+ return subExpression;
+ nodeFactory.markEndPosition();
+ m_scanner->next();
+ return nodeFactory.createNode<UnaryOperation>(token, subExpression, false);
+ }
+}
+
+ASTPointer<Expression> Parser::parseLeftHandSideExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+)
+{
+ ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
+ ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
+
+ ASTPointer<Expression> expression;
+ if (_lookAheadIndexAccessStructure)
+ expression = _lookAheadIndexAccessStructure;
+ else if (m_scanner->currentToken() == Token::New)
+ {
+ expectToken(Token::New);
+ ASTPointer<TypeName> contractName(parseTypeName(false));
+ nodeFactory.setEndPositionFromNode(contractName);
+ expression = nodeFactory.createNode<NewExpression>(contractName);
+ }
+ else
+ expression = parsePrimaryExpression();
+
+ while (true)
+ {
+ switch (m_scanner->currentToken())
+ {
+ case Token::LBrack:
+ {
+ m_scanner->next();
+ ASTPointer<Expression> index;
+ if (m_scanner->currentToken() != Token::RBrack)
+ index = parseExpression();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RBrack);
+ expression = nodeFactory.createNode<IndexAccess>(expression, index);
+ }
+ break;
+ case Token::Period:
+ {
+ m_scanner->next();
+ nodeFactory.markEndPosition();
+ expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken());
+ }
+ break;
+ case Token::LParen:
+ {
+ m_scanner->next();
+ vector<ASTPointer<Expression>> arguments;
+ vector<ASTPointer<ASTString>> names;
+ std::tie(arguments, names) = parseFunctionCallArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ expression = nodeFactory.createNode<FunctionCall>(expression, arguments, names);
+ }
+ break;
+ default:
+ return expression;
+ }
+ }
+}
+
+ASTPointer<Expression> Parser::parsePrimaryExpression()
+{
+ ASTNodeFactory nodeFactory(*this);
+ Token::Value token = m_scanner->currentToken();
+ ASTPointer<Expression> expression;
+
+ switch (token)
+ {
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
+ nodeFactory.markEndPosition();
+ expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
+ break;
+ case Token::Number:
+ if (Token::isEtherSubdenomination(m_scanner->peekNextToken()))
+ {
+ ASTPointer<ASTString> literal = getLiteralAndAdvance();
+ nodeFactory.markEndPosition();
+ Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
+ m_scanner->next();
+ expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
+ break;
+ }
+ if (Token::isTimeSubdenomination(m_scanner->peekNextToken()))
+ {
+ ASTPointer<ASTString> literal = getLiteralAndAdvance();
+ nodeFactory.markEndPosition();
+ Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
+ m_scanner->next();
+ expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
+ break;
+ }
+ // fall-through
+ case Token::StringLiteral:
+ nodeFactory.markEndPosition();
+ expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
+ break;
+ case Token::Identifier:
+ nodeFactory.markEndPosition();
+ expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
+ break;
+ case Token::LParen:
+ case Token::LBrack:
+ {
+ // Tuple/parenthesized expression or inline array/bracketed expression.
+ // Special cases: ()/[] is empty tuple/array type, (x) is not a real tuple,
+ // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples.
+ m_scanner->next();
+ vector<ASTPointer<Expression>> components;
+ Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack);
+ bool isArray = (token == Token::LBrack);
+
+ if (m_scanner->currentToken() != oppositeToken)
+ while (true)
+ {
+ if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
+ components.push_back(parseExpression());
+ else if (isArray)
+ parserError("Expected expression (inline array elements cannot be omitted).");
+ else
+ components.push_back(ASTPointer<Expression>());
+ if (m_scanner->currentToken() == oppositeToken)
+ break;
+ else if (m_scanner->currentToken() == Token::Comma)
+ m_scanner->next();
+ }
+ nodeFactory.markEndPosition();
+ expectToken(oppositeToken);
+ return nodeFactory.createNode<TupleExpression>(components, isArray);
+ }
+
+ default:
+ if (Token::isElementaryTypeName(token))
+ {
+ //used for casts
+ unsigned firstSize;
+ unsigned secondSize;
+ tie(firstSize, secondSize) = m_scanner->currentTokenInfo();
+ ElementaryTypeNameToken elementaryExpression(m_scanner->currentToken(), firstSize, secondSize);
+ expression = nodeFactory.createNode<ElementaryTypeNameExpression>(elementaryExpression);
+ m_scanner->next();
+ }
+ else
+ fatalParserError(string("Expected primary expression."));
+ break;
+ }
+ return expression;
+}
+
+vector<ASTPointer<Expression>> Parser::parseFunctionCallListArguments()
+{
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->currentToken() != Token::RParen)
+ {
+ arguments.push_back(parseExpression());
+ while (m_scanner->currentToken() != Token::RParen)
+ {
+ expectToken(Token::Comma);
+ arguments.push_back(parseExpression());
+ }
+ }
+ return arguments;
+}
+
+pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::parseFunctionCallArguments()
+{
+ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret;
+ Token::Value token = m_scanner->currentToken();
+ if (token == Token::LBrace)
+ {
+ // call({arg1 : 1, arg2 : 2 })
+ expectToken(Token::LBrace);
+ while (m_scanner->currentToken() != Token::RBrace)
+ {
+ ret.second.push_back(expectIdentifierToken());
+ expectToken(Token::Colon);
+ ret.first.push_back(parseExpression());
+
+ if (m_scanner->currentToken() == Token::Comma)
+ expectToken(Token::Comma);
+ else
+ break;
+ }
+ expectToken(Token::RBrace);
+ }
+ else
+ ret.first = parseFunctionCallListArguments();
+ return ret;
+}
+
+Parser::LookAheadInfo Parser::peekStatementType() const
+{
+ // Distinguish between variable declaration (and potentially assignment) and expression statement
+ // (which include assignments to other expressions and pre-declared variables).
+ // We have a variable declaration if we get a keyword that specifies a type name.
+ // If it is an identifier or an elementary type name followed by an identifier, we also have
+ // a variable declaration.
+ // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;").
+ // In all other cases, we have an expression statement.
+ Token::Value token(m_scanner->currentToken());
+ bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
+
+ if (token == Token::Mapping || token == Token::Var)
+ return LookAheadInfo::VariableDeclarationStatement;
+ if (mightBeTypeName)
+ {
+ Token::Value next = m_scanner->peekNextToken();
+ if (next == Token::Identifier || Token::isLocationSpecifier(next))
+ return LookAheadInfo::VariableDeclarationStatement;
+ if (next == Token::LBrack || next == Token::Period)
+ return LookAheadInfo::IndexAccessStructure;
+ }
+ return LookAheadInfo::ExpressionStatement;
+}
+
+ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
+ vector<ASTPointer<PrimaryExpression>> const& _path,
+ vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
+)
+{
+ solAssert(!_path.empty(), "");
+ ASTNodeFactory nodeFactory(*this);
+ SourceLocation location = _path.front()->location();
+ location.end = _path.back()->location().end;
+ nodeFactory.setLocation(location);
+
+ ASTPointer<TypeName> type;
+ if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_path.front().get()))
+ {
+ solAssert(_path.size() == 1, "");
+ type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeName());
+ }
+ else
+ {
+ vector<ASTString> path;
+ for (auto const& el: _path)
+ path.push_back(dynamic_cast<Identifier const&>(*el).name());
+ type = nodeFactory.createNode<UserDefinedTypeName>(path);
+ }
+ for (auto const& lengthExpression: _indices)
+ {
+ nodeFactory.setLocation(lengthExpression.second);
+ type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
+ }
+ return type;
+}
+
+ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
+ vector<ASTPointer<PrimaryExpression>> const& _path,
+ vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
+)
+{
+ solAssert(!_path.empty(), "");
+ ASTNodeFactory nodeFactory(*this, _path.front());
+ ASTPointer<Expression> expression(_path.front());
+ for (size_t i = 1; i < _path.size(); ++i)
+ {
+ SourceLocation location(_path.front()->location());
+ location.end = _path[i]->location().end;
+ nodeFactory.setLocation(location);
+ Identifier const& identifier = dynamic_cast<Identifier const&>(*_path[i]);
+ expression = nodeFactory.createNode<MemberAccess>(
+ expression,
+ make_shared<ASTString>(identifier.name())
+ );
+ }
+ for (auto const& index: _indices)
+ {
+ nodeFactory.setLocation(index.second);
+ expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
+ }
+ return expression;
+}
+
+ASTPointer<ParameterList> Parser::createEmptyParameterList()
+{
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.setLocationEmpty();
+ return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
+}
+
+}
+}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
new file mode 100644
index 00000000..a093cc5b
--- /dev/null
+++ b/libsolidity/parsing/Parser.h
@@ -0,0 +1,147 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity parser.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/parsing/ParserBase.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Scanner;
+
+class Parser: public ParserBase
+{
+public:
+ Parser(ErrorList& _errors): ParserBase(_errors) {}
+
+ ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
+
+private:
+ class ASTNodeFactory;
+
+ struct VarDeclParserOptions
+ {
+ VarDeclParserOptions() {}
+ bool allowVar = false;
+ bool isStateVariable = false;
+ bool allowIndexed = false;
+ bool allowEmptyName = false;
+ bool allowInitialValue = false;
+ bool allowLocationSpecifier = false;
+ };
+
+ ///@{
+ ///@name Parsing functions for the AST nodes
+ ASTPointer<ImportDirective> parseImportDirective();
+ ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
+ ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
+ Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
+ ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
+ ASTPointer<StructDefinition> parseStructDefinition();
+ ASTPointer<EnumDefinition> parseEnumDefinition();
+ ASTPointer<EnumValue> parseEnumValue();
+ ASTPointer<VariableDeclaration> parseVariableDeclaration(
+ VarDeclParserOptions const& _options = VarDeclParserOptions(),
+ ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
+ );
+ ASTPointer<ModifierDefinition> parseModifierDefinition();
+ ASTPointer<EventDefinition> parseEventDefinition();
+ ASTPointer<UsingForDirective> parseUsingDirective();
+ ASTPointer<ModifierInvocation> parseModifierInvocation();
+ ASTPointer<Identifier> parseIdentifier();
+ ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName();
+ ASTPointer<TypeName> parseTypeName(bool _allowVar);
+ ASTPointer<Mapping> parseMapping();
+ ASTPointer<ParameterList> parseParameterList(
+ VarDeclParserOptions const& _options,
+ bool _allowEmpty = true
+ );
+ ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {});
+ ASTPointer<Statement> parseStatement();
+ ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
+ ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
+ ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
+ /// A "simple statement" can be a variable declaration statement or an expression statement.
+ ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
+ ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
+ );
+ ASTPointer<ExpressionStatement> parseExpressionStatement(
+ ASTPointer<ASTString> const& _docString,
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ );
+ ASTPointer<Expression> parseExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ );
+ ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ );
+ ASTPointer<Expression> parseUnaryExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ );
+ ASTPointer<Expression> parseLeftHandSideExpression(
+ ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ );
+ ASTPointer<Expression> parsePrimaryExpression();
+ std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
+ std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
+ ///@}
+
+ ///@{
+ ///@name Helper functions
+
+ /// Used as return value of @see peekStatementType.
+ enum class LookAheadInfo
+ {
+ IndexAccessStructure, VariableDeclarationStatement, ExpressionStatement
+ };
+
+ /// Performs limited look-ahead to distinguish between variable declaration and expression statement.
+ /// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
+ /// decide with constant look-ahead.
+ LookAheadInfo peekStatementType() const;
+ /// @returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]".
+ ASTPointer<TypeName> typeNameIndexAccessStructure(
+ std::vector<ASTPointer<PrimaryExpression>> const& _path,
+ std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
+ );
+ /// @returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]".
+ ASTPointer<Expression> expressionFromIndexAccessStructure(
+ std::vector<ASTPointer<PrimaryExpression>> const& _path,
+ std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
+ );
+ ///@}
+
+ /// Creates an empty ParameterList at the current location (used if parameters can be omitted).
+ ASTPointer<ParameterList> createEmptyParameterList();
+
+ /// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
+ bool m_insideModifier = false;
+};
+
+}
+}
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
new file mode 100644
index 00000000..64e42841
--- /dev/null
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -0,0 +1,103 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2016
+ * Solidity parser shared functionality.
+ */
+
+#include <libsolidity/parsing/ParserBase.h>
+#include <libsolidity/parsing/Scanner.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+std::shared_ptr<string const> const& ParserBase::sourceName() const
+{
+ return m_scanner->sourceName();
+}
+
+int ParserBase::position() const
+{
+ return m_scanner->currentLocation().start;
+}
+
+int ParserBase::endPosition() const
+{
+ return m_scanner->currentLocation().end;
+}
+
+void ParserBase::expectToken(Token::Value _value)
+{
+ if (m_scanner->currentToken() != _value)
+ fatalParserError(
+ string("Expected token ") +
+ string(Token::name(_value)) +
+ string(" got '") +
+ string(Token::name(m_scanner->currentToken())) +
+ string("'")
+ );
+ m_scanner->next();
+}
+
+Token::Value ParserBase::expectAssignmentOperator()
+{
+ Token::Value op = m_scanner->currentToken();
+ if (!Token::isAssignmentOp(op))
+ fatalParserError(
+ string("Expected assignment operator, got '") +
+ string(Token::name(m_scanner->currentToken())) +
+ string("'")
+ );
+ m_scanner->next();
+ return op;
+}
+
+ASTPointer<ASTString> ParserBase::expectIdentifierToken()
+{
+ if (m_scanner->currentToken() != Token::Identifier)
+ fatalParserError(
+ string("Expected identifier, got '") +
+ string(Token::name(m_scanner->currentToken())) +
+ string("'")
+ );
+ return getLiteralAndAdvance();
+}
+
+ASTPointer<ASTString> ParserBase::getLiteralAndAdvance()
+{
+ ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->currentLiteral());
+ m_scanner->next();
+ return identifier;
+}
+
+void ParserBase::parserError(string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::ParserError);
+ *err <<
+ errinfo_sourceLocation(SourceLocation(position(), position(), sourceName())) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void ParserBase::fatalParserError(string const& _description)
+{
+ parserError(_description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
new file mode 100644
index 00000000..9705228f
--- /dev/null
+++ b/libsolidity/parsing/ParserBase.h
@@ -0,0 +1,74 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2016
+ * Solidity parser shared functionality.
+ */
+
+#pragma once
+
+#include <memory>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/parsing/Token.h>
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Scanner;
+
+class ParserBase
+{
+public:
+ ParserBase(ErrorList& errors): m_errors(errors) {}
+
+ std::shared_ptr<std::string const> const& sourceName() const;
+
+protected:
+ /// Start position of the current token
+ int position() const;
+ /// End position of the current token
+ int endPosition() const;
+
+
+ ///@{
+ ///@name Helper functions
+ /// If current token value is not _value, throw exception otherwise advance token.
+ void expectToken(Token::Value _value);
+ Token::Value expectAssignmentOperator();
+ ASTPointer<ASTString> expectIdentifierToken();
+ ASTPointer<ASTString> getLiteralAndAdvance();
+ ///@}
+
+ /// Creates a @ref ParserError and annotates it with the current position and the
+ /// given @a _description.
+ void parserError(std::string const& _description);
+
+ /// Creates a @ref ParserError and annotates it with the current position and the
+ /// given @a _description. Throws the FatalError.
+ void fatalParserError(std::string const& _description);
+
+ std::shared_ptr<Scanner> m_scanner;
+ /// The reference to the list of errors and warning to add errors/warnings during parsing
+ ErrorList& m_errors;
+};
+
+}
+}
diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
new file mode 100644
index 00000000..d630d0ab
--- /dev/null
+++ b/libsolidity/parsing/Scanner.cpp
@@ -0,0 +1,777 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+ This file is derived from the file "scanner.cc", which was part of the
+ V8 project. The original copyright header follows:
+
+ Copyright 2006-2012, the V8 project authors. All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity scanner.
+ */
+
+#include <algorithm>
+#include <tuple>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/parsing/Scanner.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+namespace
+{
+bool isDecimalDigit(char c)
+{
+ return '0' <= c && c <= '9';
+}
+bool isHexDigit(char c)
+{
+ return isDecimalDigit(c)
+ || ('a' <= c && c <= 'f')
+ || ('A' <= c && c <= 'F');
+}
+bool isLineTerminator(char c)
+{
+ return c == '\n';
+}
+bool isWhiteSpace(char c)
+{
+ return c == ' ' || c == '\n' || c == '\t' || c == '\r';
+}
+bool isIdentifierStart(char c)
+{
+ return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+}
+bool isIdentifierPart(char c)
+{
+ return isIdentifierStart(c) || isDecimalDigit(c);
+}
+int hexValue(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else return -1;
+}
+} // end anonymous namespace
+
+
+
+/// Scoped helper for literal recording. Automatically drops the literal
+/// if aborting the scanning before it's complete.
+enum LiteralType {
+ LITERAL_TYPE_STRING,
+ LITERAL_TYPE_NUMBER, // not really different from string type in behaviour
+ LITERAL_TYPE_COMMENT
+};
+
+class LiteralScope
+{
+public:
+ explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type)
+ , m_scanner(_self)
+ , m_complete(false)
+ {
+ if (_type == LITERAL_TYPE_COMMENT)
+ m_scanner->m_nextSkippedComment.literal.clear();
+ else
+ m_scanner->m_nextToken.literal.clear();
+ }
+ ~LiteralScope()
+ {
+ if (!m_complete)
+ {
+ if (m_type == LITERAL_TYPE_COMMENT)
+ m_scanner->m_nextSkippedComment.literal.clear();
+ else
+ m_scanner->m_nextToken.literal.clear();
+ }
+ }
+ void complete() { m_complete = true; }
+
+private:
+ enum LiteralType m_type;
+ Scanner* m_scanner;
+ bool m_complete;
+}; // end of LiteralScope class
+
+
+void Scanner::reset(CharStream const& _source, string const& _sourceName)
+{
+ m_source = _source;
+ m_sourceName = make_shared<string const>(_sourceName);
+ reset();
+}
+
+void Scanner::reset()
+{
+ m_source.reset();
+ m_char = m_source.get();
+ skipWhitespace();
+ scanToken();
+ next();
+}
+
+bool Scanner::scanHexByte(char& o_scannedByte)
+{
+ char x = 0;
+ for (int i = 0; i < 2; i++)
+ {
+ int d = hexValue(m_char);
+ if (d < 0)
+ {
+ rollback(i);
+ return false;
+ }
+ x = x * 16 + d;
+ advance();
+ }
+ o_scannedByte = x;
+ return true;
+}
+
+
+// Ensure that tokens can be stored in a byte.
+BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
+
+Token::Value Scanner::next()
+{
+ m_currentToken = m_nextToken;
+ m_skippedComment = m_nextSkippedComment;
+ scanToken();
+
+ return m_currentToken.token;
+}
+
+Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else)
+{
+ advance();
+ if (m_char == _next)
+ return selectToken(_then);
+ else
+ return _else;
+}
+
+bool Scanner::skipWhitespace()
+{
+ int const startPosition = sourcePos();
+ while (isWhiteSpace(m_char))
+ advance();
+ // Return whether or not we skipped any characters.
+ return sourcePos() != startPosition;
+}
+
+bool Scanner::skipWhitespaceExceptLF()
+{
+ int const startPosition = sourcePos();
+ while (isWhiteSpace(m_char) && !isLineTerminator(m_char))
+ advance();
+ // Return whether or not we skipped any characters.
+ return sourcePos() != startPosition;
+}
+
+Token::Value Scanner::skipSingleLineComment()
+{
+ // The line terminator at the end of the line is not considered
+ // to be part of the single-line comment; it is recognized
+ // separately by the lexical grammar and becomes part of the
+ // stream of input elements for the syntactic grammar
+ while (!isLineTerminator(m_char))
+ if (!advance()) break;
+
+ return Token::Whitespace;
+}
+
+Token::Value Scanner::scanSingleLineDocComment()
+{
+ LiteralScope literal(this, LITERAL_TYPE_COMMENT);
+ advance(); //consume the last '/' at ///
+ skipWhitespaceExceptLF();
+ while (!isSourcePastEndOfInput())
+ {
+ if (isLineTerminator(m_char))
+ {
+ // check if next line is also a documentation comment
+ skipWhitespace();
+ if (!m_source.isPastEndOfInput(3) &&
+ m_source.get(0) == '/' &&
+ m_source.get(1) == '/' &&
+ m_source.get(2) == '/')
+ {
+ addCommentLiteralChar('\n');
+ m_char = m_source.advanceAndGet(3);
+ }
+ else
+ break; // next line is not a documentation comment, we are done
+
+ }
+ addCommentLiteralChar(m_char);
+ advance();
+ }
+ literal.complete();
+ return Token::CommentLiteral;
+}
+
+Token::Value Scanner::skipMultiLineComment()
+{
+ advance();
+ while (!isSourcePastEndOfInput())
+ {
+ char ch = m_char;
+ advance();
+
+ // If we have reached the end of the multi-line comment, we
+ // consume the '/' and insert a whitespace. This way all
+ // multi-line comments are treated as whitespace.
+ if (ch == '*' && m_char == '/')
+ {
+ m_char = ' ';
+ return Token::Whitespace;
+ }
+ }
+ // Unterminated multi-line comment.
+ return Token::Illegal;
+}
+
+Token::Value Scanner::scanMultiLineDocComment()
+{
+ LiteralScope literal(this, LITERAL_TYPE_COMMENT);
+ bool endFound = false;
+ bool charsAdded = false;
+
+ while (!isSourcePastEndOfInput())
+ {
+ //handle newlines in multline comments
+ if (isLineTerminator(m_char))
+ {
+ skipWhitespace();
+ if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/')
+ { // skip first '*' in subsequent lines
+ if (charsAdded)
+ addCommentLiteralChar('\n');
+ m_char = m_source.advanceAndGet(2);
+ }
+ else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
+ { // if after newline the comment ends, don't insert the newline
+ m_char = m_source.advanceAndGet(2);
+ endFound = true;
+ break;
+ }
+ else if (charsAdded)
+ addCommentLiteralChar('\n');
+ }
+
+ if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
+ {
+ m_char = m_source.advanceAndGet(2);
+ endFound = true;
+ break;
+ }
+ addCommentLiteralChar(m_char);
+ charsAdded = true;
+ advance();
+ }
+ literal.complete();
+ if (!endFound)
+ return Token::Illegal;
+ else
+ return Token::CommentLiteral;
+}
+
+Token::Value Scanner::scanSlash()
+{
+ int firstSlashPosition = sourcePos();
+ advance();
+ if (m_char == '/')
+ {
+ if (!advance()) /* double slash comment directly before EOS */
+ return Token::Whitespace;
+ else if (m_char == '/')
+ {
+ // doxygen style /// comment
+ Token::Value comment;
+ m_nextSkippedComment.location.start = firstSlashPosition;
+ comment = scanSingleLineDocComment();
+ m_nextSkippedComment.location.end = sourcePos();
+ m_nextSkippedComment.token = comment;
+ return Token::Whitespace;
+ }
+ else
+ return skipSingleLineComment();
+ }
+ else if (m_char == '*')
+ {
+ // doxygen style /** natspec comment
+ if (!advance()) /* slash star comment before EOS */
+ return Token::Whitespace;
+ else if (m_char == '*')
+ {
+ advance(); //consume the last '*' at /**
+ skipWhitespaceExceptLF();
+
+ // special case of a closed normal multiline comment
+ if (!m_source.isPastEndOfInput() && m_source.get(0) == '/')
+ advance(); //skip the closing slash
+ else // we actually have a multiline documentation comment
+ {
+ Token::Value comment;
+ m_nextSkippedComment.location.start = firstSlashPosition;
+ comment = scanMultiLineDocComment();
+ m_nextSkippedComment.location.end = sourcePos();
+ m_nextSkippedComment.token = comment;
+ }
+ return Token::Whitespace;
+ }
+ else
+ return skipMultiLineComment();
+ }
+ else if (m_char == '=')
+ return selectToken(Token::AssignDiv);
+ else
+ return Token::Div;
+}
+
+void Scanner::scanToken()
+{
+ m_nextToken.literal.clear();
+ m_nextToken.extendedTokenInfo = make_tuple(0, 0);
+ m_nextSkippedComment.literal.clear();
+ m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0);
+
+ Token::Value token;
+ // M and N are for the purposes of grabbing different type sizes
+ unsigned m;
+ unsigned n;
+ do
+ {
+ // Remember the position of the next token
+ m_nextToken.location.start = sourcePos();
+ switch (m_char)
+ {
+ case '\n': // fall-through
+ case ' ':
+ case '\t':
+ token = selectToken(Token::Whitespace);
+ break;
+ case '"':
+ case '\'':
+ token = scanString();
+ break;
+ case '<':
+ // < <= << <<=
+ advance();
+ if (m_char == '=')
+ token = selectToken(Token::LessThanOrEqual);
+ else if (m_char == '<')
+ token = selectToken('=', Token::AssignShl, Token::SHL);
+ else
+ token = Token::LessThan;
+ break;
+ case '>':
+ // > >= >> >>= >>> >>>=
+ advance();
+ if (m_char == '=')
+ token = selectToken(Token::GreaterThanOrEqual);
+ else if (m_char == '>')
+ {
+ // >> >>= >>> >>>=
+ advance();
+ if (m_char == '=')
+ token = selectToken(Token::AssignSar);
+ else if (m_char == '>')
+ token = selectToken('=', Token::AssignShr, Token::SHR);
+ else
+ token = Token::SAR;
+ }
+ else
+ token = Token::GreaterThan;
+ break;
+ case '=':
+ // = == =>
+ advance();
+ if (m_char == '=')
+ token = selectToken(Token::Equal);
+ else if (m_char == '>')
+ token = selectToken(Token::Arrow);
+ else
+ token = Token::Assign;
+ break;
+ case '!':
+ // ! !=
+ advance();
+ if (m_char == '=')
+ token = selectToken(Token::NotEqual);
+ else
+ token = Token::Not;
+ break;
+ case '+':
+ // + ++ +=
+ advance();
+ if (m_char == '+')
+ token = selectToken(Token::Inc);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignAdd);
+ else
+ token = Token::Add;
+ break;
+ case '-':
+ // - -- -=
+ advance();
+ if (m_char == '-')
+ token = selectToken(Token::Dec);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignSub);
+ else
+ token = Token::Sub;
+ break;
+ case '*':
+ // * ** *=
+ advance();
+ if (m_char == '*')
+ token = selectToken(Token::Exp);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignMul);
+ else
+ token = Token::Mul;
+ break;
+ case '%':
+ // % %=
+ token = selectToken('=', Token::AssignMod, Token::Mod);
+ break;
+ case '/':
+ // / // /* /=
+ token = scanSlash();
+ break;
+ case '&':
+ // & && &=
+ advance();
+ if (m_char == '&')
+ token = selectToken(Token::And);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignBitAnd);
+ else
+ token = Token::BitAnd;
+ break;
+ case '|':
+ // | || |=
+ advance();
+ if (m_char == '|')
+ token = selectToken(Token::Or);
+ else if (m_char == '=')
+ token = selectToken(Token::AssignBitOr);
+ else
+ token = Token::BitOr;
+ break;
+ case '^':
+ // ^ ^=
+ token = selectToken('=', Token::AssignBitXor, Token::BitXor);
+ break;
+ case '.':
+ // . Number
+ advance();
+ if (isDecimalDigit(m_char))
+ token = scanNumber('.');
+ else
+ token = Token::Period;
+ break;
+ case ':':
+ token = selectToken(Token::Colon);
+ break;
+ case ';':
+ token = selectToken(Token::Semicolon);
+ break;
+ case ',':
+ token = selectToken(Token::Comma);
+ break;
+ case '(':
+ token = selectToken(Token::LParen);
+ break;
+ case ')':
+ token = selectToken(Token::RParen);
+ break;
+ case '[':
+ token = selectToken(Token::LBrack);
+ break;
+ case ']':
+ token = selectToken(Token::RBrack);
+ break;
+ case '{':
+ token = selectToken(Token::LBrace);
+ break;
+ case '}':
+ token = selectToken(Token::RBrace);
+ break;
+ case '?':
+ token = selectToken(Token::Conditional);
+ break;
+ case '~':
+ token = selectToken(Token::BitNot);
+ break;
+ default:
+ if (isIdentifierStart(m_char))
+ tie(token, m, n) = scanIdentifierOrKeyword();
+ else if (isDecimalDigit(m_char))
+ token = scanNumber();
+ else if (skipWhitespace())
+ token = Token::Whitespace;
+ else if (isSourcePastEndOfInput())
+ token = Token::EOS;
+ else
+ token = selectToken(Token::Illegal);
+ break;
+ }
+ // Continue scanning for tokens as long as we're just skipping
+ // whitespace.
+ }
+ while (token == Token::Whitespace);
+ m_nextToken.location.end = sourcePos();
+ m_nextToken.token = token;
+ m_nextToken.extendedTokenInfo = make_tuple(m, n);
+}
+
+bool Scanner::scanEscape()
+{
+ char c = m_char;
+ advance();
+ // Skip escaped newlines.
+ if (isLineTerminator(c))
+ return true;
+ switch (c)
+ {
+ case '\'': // fall through
+ case '"': // fall through
+ case '\\':
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'x':
+ if (!scanHexByte(c))
+ return false;
+ break;
+ }
+
+ addLiteralChar(c);
+ return true;
+}
+
+Token::Value Scanner::scanString()
+{
+ char const quote = m_char;
+ advance(); // consume quote
+ LiteralScope literal(this, LITERAL_TYPE_STRING);
+ while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char))
+ {
+ char c = m_char;
+ advance();
+ if (c == '\\')
+ {
+ if (isSourcePastEndOfInput() || !scanEscape())
+ return Token::Illegal;
+ }
+ else
+ addLiteralChar(c);
+ }
+ if (m_char != quote)
+ return Token::Illegal;
+ literal.complete();
+ advance(); // consume quote
+ return Token::StringLiteral;
+}
+
+void Scanner::scanDecimalDigits()
+{
+ while (isDecimalDigit(m_char))
+ addLiteralCharAndAdvance();
+}
+
+Token::Value Scanner::scanNumber(char _charSeen)
+{
+ enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
+ LiteralScope literal(this, LITERAL_TYPE_NUMBER);
+ if (_charSeen == '.')
+ {
+ // we have already seen a decimal point of the float
+ addLiteralChar('.');
+ scanDecimalDigits(); // we know we have at least one digit
+ }
+ else
+ {
+ solAssert(_charSeen == 0, "");
+ // if the first character is '0' we must check for octals and hex
+ if (m_char == '0')
+ {
+ addLiteralCharAndAdvance();
+ // either 0, 0exxx, 0Exxx, 0.xxx or a hex number
+ if (m_char == 'x' || m_char == 'X')
+ {
+ // hex number
+ kind = HEX;
+ addLiteralCharAndAdvance();
+ if (!isHexDigit(m_char))
+ return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
+ while (isHexDigit(m_char))
+ addLiteralCharAndAdvance();
+ }
+ }
+ // Parse decimal digits and allow trailing fractional part.
+ if (kind == DECIMAL)
+ {
+ scanDecimalDigits(); // optional
+ if (m_char == '.')
+ {
+ addLiteralCharAndAdvance();
+ scanDecimalDigits(); // optional
+ }
+ }
+ }
+ // scan exponent, if any
+ if (m_char == 'e' || m_char == 'E')
+ {
+ solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
+ if (kind != DECIMAL)
+ return Token::Illegal;
+ // scan exponent
+ addLiteralCharAndAdvance();
+ if (m_char == '+' || m_char == '-')
+ addLiteralCharAndAdvance();
+ if (!isDecimalDigit(m_char))
+ return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
+ scanDecimalDigits();
+ }
+ // The source character immediately following a numeric literal must
+ // not be an identifier start or a decimal digit; see ECMA-262
+ // section 7.8.3, page 17 (note that we read only one decimal digit
+ // if the value is 0).
+ if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
+ return Token::Illegal;
+ literal.complete();
+ return Token::Number;
+}
+
+tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
+{
+ solAssert(isIdentifierStart(m_char), "");
+ LiteralScope literal(this, LITERAL_TYPE_STRING);
+ addLiteralCharAndAdvance();
+ // Scan the rest of the identifier characters.
+ while (isIdentifierPart(m_char)) //get full literal
+ addLiteralCharAndAdvance();
+ literal.complete();
+ return Token::fromIdentifierOrKeyword(m_nextToken.literal);
+}
+
+char CharStream::advanceAndGet(size_t _chars)
+{
+ if (isPastEndOfInput())
+ return 0;
+ m_position += _chars;
+ if (isPastEndOfInput())
+ return 0;
+ return m_source[m_position];
+}
+
+char CharStream::rollback(size_t _amount)
+{
+ solAssert(m_position >= _amount, "");
+ m_position -= _amount;
+ return get();
+}
+
+string CharStream::lineAtPosition(int _position) const
+{
+ // if _position points to \n, it returns the line before the \n
+ using size_type = string::size_type;
+ size_type searchStart = min<size_type>(m_source.size(), _position);
+ if (searchStart > 0)
+ searchStart--;
+ size_type lineStart = m_source.rfind('\n', searchStart);
+ if (lineStart == string::npos)
+ lineStart = 0;
+ else
+ lineStart++;
+ return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
+ m_source.size()) - lineStart);
+}
+
+tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
+{
+ using size_type = string::size_type;
+ size_type searchPosition = min<size_type>(m_source.size(), _position);
+ int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
+ size_type lineStart;
+ if (searchPosition == 0)
+ lineStart = 0;
+ else
+ {
+ lineStart = m_source.rfind('\n', searchPosition - 1);
+ lineStart = lineStart == string::npos ? 0 : lineStart + 1;
+ }
+ return tuple<int, int>(lineNumber, searchPosition - lineStart);
+}
+
+
+}
+}
diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h
new file mode 100644
index 00000000..cffcec8e
--- /dev/null
+++ b/libsolidity/parsing/Scanner.h
@@ -0,0 +1,226 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+ This file is derived from the file "scanner.h", which was part of the
+ V8 project. The original copyright header follows:
+
+ Copyright 2006-2012, the V8 project authors. All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity scanner.
+ */
+
+#pragma once
+
+#include <libdevcore/Common.h>
+#include <libdevcore/Log.h>
+#include <libdevcore/CommonData.h>
+#include <libevmasm/SourceLocation.h>
+#include <libsolidity/parsing/Token.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+
+class AstRawString;
+class AstValueFactory;
+class ParserRecorder;
+
+class CharStream
+{
+public:
+ CharStream(): m_position(0) {}
+ explicit CharStream(std::string const& _source): m_source(_source), m_position(0) {}
+ int position() const { return m_position; }
+ bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
+ char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; }
+ char advanceAndGet(size_t _chars=1);
+ char rollback(size_t _amount);
+
+ void reset() { m_position = 0; }
+
+ ///@{
+ ///@name Error printing helper functions
+ /// Functions that help pretty-printing parse errors
+ /// Do only use in error cases, they are quite expensive.
+ std::string lineAtPosition(int _position) const;
+ std::tuple<int, int> translatePositionToLineColumn(int _position) const;
+ ///@}
+
+private:
+ std::string m_source;
+ size_t m_position;
+};
+
+
+
+class Scanner
+{
+ friend class LiteralScope;
+public:
+
+ explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); }
+
+ /// Resets the scanner as if newly constructed with _source and _sourceName as input.
+ void reset(CharStream const& _source, std::string const& _sourceName);
+ /// Resets scanner to the start of input.
+ void reset();
+
+ /// @returns the next token and advances input
+ Token::Value next();
+
+ ///@{
+ ///@name Information about the current token
+
+ /// @returns the current token
+ Token::Value currentToken()
+ {
+ return m_currentToken.token;
+ }
+
+ SourceLocation currentLocation() const { return m_currentToken.location; }
+ std::string const& currentLiteral() const { return m_currentToken.literal; }
+ std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_currentToken.extendedTokenInfo; }
+ ///@}
+
+ ///@{
+ ///@name Information about the current comment token
+
+ SourceLocation currentCommentLocation() const { return m_skippedComment.location; }
+ std::string const& currentCommentLiteral() const { return m_skippedComment.literal; }
+ /// Called by the parser during FunctionDefinition parsing to clear the current comment
+ void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
+
+ ///@}
+
+ ///@{
+ ///@name Information about the next token
+
+ /// @returns the next token without advancing input.
+ Token::Value peekNextToken() const { return m_nextToken.token; }
+ SourceLocation peekLocation() const { return m_nextToken.location; }
+ std::string const& peekLiteral() const { return m_nextToken.literal; }
+ ///@}
+
+ std::shared_ptr<std::string const> const& sourceName() const { return m_sourceName; }
+
+ ///@{
+ ///@name Error printing helper functions
+ /// Functions that help pretty-printing parse errors
+ /// Do only use in error cases, they are quite expensive.
+ std::string lineAtPosition(int _position) const { return m_source.lineAtPosition(_position); }
+ std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
+ ///@}
+
+private:
+ /// Used for the current and look-ahead token and comments
+ struct TokenDesc
+ {
+ Token::Value token;
+ SourceLocation location;
+ std::string literal;
+ std::tuple<unsigned, unsigned> extendedTokenInfo;
+ };
+
+ ///@{
+ ///@name Literal buffer support
+ inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
+ inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
+ inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
+ ///@}
+
+ bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
+ void rollback(int _amount) { m_char = m_source.rollback(_amount); }
+
+ inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; }
+ /// If the next character is _next, advance and return _then, otherwise return _else.
+ inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else);
+
+ bool scanHexByte(char& o_scannedByte);
+
+ /// Scans a single Solidity token.
+ void scanToken();
+
+ /// Skips all whitespace and @returns true if something was skipped.
+ bool skipWhitespace();
+ /// Skips all whitespace except Line feeds and returns true if something was skipped
+ bool skipWhitespaceExceptLF();
+ Token::Value skipSingleLineComment();
+ Token::Value skipMultiLineComment();
+
+ void scanDecimalDigits();
+ Token::Value scanNumber(char _charSeen = 0);
+ std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword();
+
+ Token::Value scanString();
+ Token::Value scanSingleLineDocComment();
+ Token::Value scanMultiLineDocComment();
+ /// Scans a slash '/' and depending on the characters returns the appropriate token
+ Token::Value scanSlash();
+
+ /// Scans an escape-sequence which is part of a string and adds the
+ /// decoded character to the current literal. Returns true if a pattern
+ /// is scanned.
+ bool scanEscape();
+
+ /// Return the current source position.
+ int sourcePos() { return m_source.position(); }
+ bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); }
+
+ TokenDesc m_skippedComment; // desc for current skipped comment
+ TokenDesc m_nextSkippedComment; // desc for next skiped comment
+
+ TokenDesc m_currentToken; // desc for current token (as returned by Next())
+ TokenDesc m_nextToken; // desc for next token (one token look-ahead)
+
+ CharStream m_source;
+ std::shared_ptr<std::string const> m_sourceName;
+
+ /// one character look-ahead, equals 0 at end of input
+ char m_char;
+};
+
+}
+}
diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp
new file mode 100644
index 00000000..3812a83f
--- /dev/null
+++ b/libsolidity/parsing/Token.cpp
@@ -0,0 +1,191 @@
+// Copyright 2006-2012, the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Modifications as part of cpp-ethereum under the following license:
+//
+// cpp-ethereum 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.
+//
+// cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+#include <map>
+#include <libsolidity/parsing/Token.h>
+#include <boost/range/iterator_range.hpp>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second)
+{
+ solAssert(Token::isElementaryTypeName(_baseType), "");
+ if (_baseType == Token::BytesM)
+ {
+ solAssert(_second == 0, "There should not be a second size argument to type bytesM.");
+ solAssert(_first <= 32, "No elementary type bytes" + to_string(_first) + ".");
+ }
+ else if (_baseType == Token::UIntM || _baseType == Token::IntM)
+ {
+ solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + ".");
+ solAssert(
+ _first <= 256 && _first % 8 == 0,
+ "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "."
+ );
+ }
+ else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN)
+ {
+ solAssert(
+ _first + _second <= 256 && _first % 8 == 0 && _second % 8 == 0,
+ "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "."
+ );
+ }
+ m_token = _baseType;
+ m_firstNumber = _first;
+ m_secondNumber = _second;
+}
+
+#define T(name, string, precedence) #name,
+char const* const Token::m_name[NUM_TOKENS] =
+{
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define T(name, string, precedence) string,
+char const* const Token::m_string[NUM_TOKENS] =
+{
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define T(name, string, precedence) precedence,
+int8_t const Token::m_precedence[NUM_TOKENS] =
+{
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define KT(a, b, c) 'T',
+#define KK(a, b, c) 'K',
+char const Token::m_tokenType[] =
+{
+ TOKEN_LIST(KT, KK)
+};
+int Token::parseSize(string::const_iterator _begin, string::const_iterator _end)
+{
+ try
+ {
+ unsigned int m = boost::lexical_cast<int>(boost::make_iterator_range(_begin, _end));
+ return m;
+ }
+ catch(boost::bad_lexical_cast const&)
+ {
+ return -1;
+ }
+}
+tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal)
+{
+ auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit);
+ if (positionM != _literal.end())
+ {
+ string baseType(_literal.begin(), positionM);
+ auto positionX = find_if_not(positionM, _literal.end(), ::isdigit);
+ int m = parseSize(positionM, positionX);
+ Token::Value keyword = keywordByName(baseType);
+ if (keyword == Token::Bytes)
+ {
+ if (0 < m && m <= 32 && positionX == _literal.end())
+ return make_tuple(Token::BytesM, m, 0);
+ }
+ else if (keyword == Token::UInt || keyword == Token::Int)
+ {
+ if (0 < m && m <= 256 && m % 8 == 0 && positionX == _literal.end())
+ {
+ if (keyword == Token::UInt)
+ return make_tuple(Token::UIntM, m, 0);
+ else
+ return make_tuple(Token::IntM, m, 0);
+ }
+ }
+ else if (keyword == Token::UFixed || keyword == Token::Fixed)
+ {
+ if (
+ positionM < positionX &&
+ positionX < _literal.end() &&
+ *positionX == 'x' &&
+ all_of(positionX + 1, _literal.end(), ::isdigit)
+ ) {
+ int n = parseSize(positionX + 1, _literal.end());
+ if (
+ 0 < m && m < 256 &&
+ 0 < n && n < 256 &&
+ m + n <= 256 &&
+ m % 8 == 0 &&
+ n % 8 == 0
+ ) {
+ if (keyword == Token::UFixed)
+ return make_tuple(Token::UFixed, m, n);
+ else
+ return make_tuple(Token::Fixed, m, n);
+ }
+ }
+ }
+ return make_tuple(Token::Identifier, 0, 0);
+ }
+ return make_tuple(keywordByName(_literal), 0, 0);
+}
+Token::Value Token::keywordByName(string const& _name)
+{
+ // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
+ // and keywords to be put inside the keywords variable.
+#define KEYWORD(name, string, precedence) {string, Token::name},
+#define TOKEN(name, string, precedence)
+ static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
+#undef KEYWORD
+#undef TOKEN
+ auto it = keywords.find(_name);
+ return it == keywords.end() ? Token::Identifier : it->second;
+}
+
+#undef KT
+#undef KK
+}
+}
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
new file mode 100644
index 00000000..31646f8d
--- /dev/null
+++ b/libsolidity/parsing/Token.h
@@ -0,0 +1,350 @@
+// Copyright 2006-2012, the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Modifications as part of cpp-ethereum under the following license:
+//
+// cpp-ethereum 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.
+//
+// cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <libdevcore/Common.h>
+#include <libdevcore/Log.h>
+#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libdevcore/UndefMacros.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
+// same signature M(name, string, precedence), where name is the
+// symbolic token name, string is the corresponding syntactic symbol
+// (or NULL, for literals), and precedence is the precedence (or 0).
+// The parameters are invoked for token categories as follows:
+//
+// T: Non-keyword tokens
+// K: Keyword tokens
+
+// IGNORE_TOKEN is a convenience macro that can be supplied as
+// an argument (at any position) for a TOKEN_LIST call. It does
+// nothing with tokens belonging to the respective category.
+
+#define IGNORE_TOKEN(name, string, precedence)
+
+#define TOKEN_LIST(T, K) \
+ /* End of source indicator. */ \
+ T(EOS, "EOS", 0) \
+ \
+ /* Punctuators (ECMA-262, section 7.7, page 15). */ \
+ T(LParen, "(", 0) \
+ T(RParen, ")", 0) \
+ T(LBrack, "[", 0) \
+ T(RBrack, "]", 0) \
+ T(LBrace, "{", 0) \
+ T(RBrace, "}", 0) \
+ T(Colon, ":", 0) \
+ T(Semicolon, ";", 0) \
+ T(Period, ".", 0) \
+ T(Conditional, "?", 3) \
+ T(Arrow, "=>", 0) \
+ \
+ /* Assignment operators. */ \
+ /* IsAssignmentOp() relies on this block of enum values being */ \
+ /* contiguous and sorted in the same order!*/ \
+ T(Assign, "=", 2) \
+ /* The following have to be in exactly the same order as the simple binary operators*/ \
+ T(AssignBitOr, "|=", 2) \
+ T(AssignBitXor, "^=", 2) \
+ T(AssignBitAnd, "&=", 2) \
+ T(AssignShl, "<<=", 2) \
+ T(AssignSar, ">>=", 2) \
+ T(AssignShr, ">>>=", 2) \
+ T(AssignAdd, "+=", 2) \
+ T(AssignSub, "-=", 2) \
+ T(AssignMul, "*=", 2) \
+ T(AssignDiv, "/=", 2) \
+ T(AssignMod, "%=", 2) \
+ \
+ /* Binary operators sorted by precedence. */ \
+ /* IsBinaryOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(Comma, ",", 1) \
+ T(Or, "||", 4) \
+ T(And, "&&", 5) \
+ T(BitOr, "|", 8) \
+ T(BitXor, "^", 9) \
+ T(BitAnd, "&", 10) \
+ T(SHL, "<<", 11) \
+ T(SAR, ">>", 11) \
+ T(SHR, ">>>", 11) \
+ T(Add, "+", 12) \
+ T(Sub, "-", 12) \
+ T(Mul, "*", 13) \
+ T(Div, "/", 13) \
+ T(Mod, "%", 13) \
+ T(Exp, "**", 14) \
+ \
+ /* Compare operators sorted by precedence. */ \
+ /* IsCompareOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(Equal, "==", 6) \
+ T(NotEqual, "!=", 6) \
+ T(LessThan, "<", 7) \
+ T(GreaterThan, ">", 7) \
+ T(LessThanOrEqual, "<=", 7) \
+ T(GreaterThanOrEqual, ">=", 7) \
+ K(In, "in", 7) \
+ \
+ /* Unary operators. */ \
+ /* IsUnaryOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(Not, "!", 0) \
+ T(BitNot, "~", 0) \
+ T(Inc, "++", 0) \
+ T(Dec, "--", 0) \
+ K(Delete, "delete", 0) \
+ \
+ /* Keywords */ \
+ K(Anonymous, "anonymous", 0) \
+ K(Assembly, "assembly", 0) \
+ K(Break, "break", 0) \
+ K(Const, "constant", 0) \
+ K(Continue, "continue", 0) \
+ K(Contract, "contract", 0) \
+ K(Default, "default", 0) \
+ K(Do, "do", 0) \
+ K(Else, "else", 0) \
+ K(Enum, "enum", 0) \
+ K(Event, "event", 0) \
+ K(External, "external", 0) \
+ K(For, "for", 0) \
+ K(Function, "function", 0) \
+ K(If, "if", 0) \
+ K(Indexed, "indexed", 0) \
+ K(Internal, "internal", 0) \
+ K(Import, "import", 0) \
+ K(Is, "is", 0) \
+ K(Library, "library", 0) \
+ K(Mapping, "mapping", 0) \
+ K(Memory, "memory", 0) \
+ K(Modifier, "modifier", 0) \
+ K(New, "new", 0) \
+ K(Public, "public", 0) \
+ K(Private, "private", 0) \
+ K(Return, "return", 0) \
+ K(Returns, "returns", 0) \
+ K(Storage, "storage", 0) \
+ K(Struct, "struct", 0) \
+ K(Throw, "throw", 0) \
+ K(Var, "var", 0) \
+ K(While, "while", 0) \
+ \
+ /* Ether subdenominations */ \
+ K(SubWei, "wei", 0) \
+ K(SubSzabo, "szabo", 0) \
+ K(SubFinney, "finney", 0) \
+ K(SubEther, "ether", 0) \
+ K(SubSecond, "seconds", 0) \
+ K(SubMinute, "minutes", 0) \
+ K(SubHour, "hours", 0) \
+ K(SubDay, "days", 0) \
+ K(SubWeek, "weeks", 0) \
+ K(SubYear, "years", 0) \
+ K(After, "after", 0) \
+ /* type keywords*/ \
+ K(Int, "int", 0) \
+ T(IntM, "intM", 0) \
+ K(UInt, "uint", 0) \
+ T(UIntM, "uintM", 0) \
+ K(Bytes, "bytes", 0) \
+ T(BytesM, "bytesM", 0) \
+ K(Byte, "byte", 0) \
+ K(String, "string", 0) \
+ K(Address, "address", 0) \
+ K(Bool, "bool", 0) \
+ K(Fixed, "fixed", 0) \
+ T(FixedMxN, "fixedMxN", 0) \
+ K(UFixed, "ufixed", 0) \
+ T(UFixedMxN, "ufixedMxN", 0) \
+ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
+ \
+ /* Literals */ \
+ K(NullLiteral, "null", 0) \
+ K(TrueLiteral, "true", 0) \
+ K(FalseLiteral, "false", 0) \
+ T(Number, NULL, 0) \
+ T(StringLiteral, NULL, 0) \
+ T(CommentLiteral, NULL, 0) \
+ \
+ /* Identifiers (not keywords or future reserved words). */ \
+ T(Identifier, NULL, 0) \
+ \
+ /* Keywords reserved for future use. */ \
+ K(As, "as", 0) \
+ K(Case, "case", 0) \
+ K(Catch, "catch", 0) \
+ K(Final, "final", 0) \
+ K(Inline, "inline", 0) \
+ K(Let, "let", 0) \
+ K(Match, "match", 0) \
+ K(Of, "of", 0) \
+ K(Relocatable, "relocatable", 0) \
+ K(Switch, "switch", 0) \
+ K(Try, "try", 0) \
+ K(Type, "type", 0) \
+ K(TypeOf, "typeof", 0) \
+ K(Using, "using", 0) \
+ /* Illegal token - not able to scan. */ \
+ T(Illegal, "ILLEGAL", 0) \
+ \
+ /* Scanner-internal use only. */ \
+ T(Whitespace, NULL, 0)
+
+
+class Token
+{
+public:
+ // All token values.
+ // attention! msvc issue:
+ // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059
+ // @todo: avoid TOKEN_LIST macro
+#define T(name, string, precedence) name,
+ enum Value
+ {
+ TOKEN_LIST(T, T)
+ NUM_TOKENS
+ };
+#undef T
+
+ // @returns a string corresponding to the C++ token name
+ // (e.g. "LT" for the token LT).
+ static char const* name(Value tok)
+ {
+ solAssert(tok < NUM_TOKENS, "");
+ return m_name[tok];
+ }
+
+ // Predicates
+ static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; }
+ static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; }
+ static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; }
+ static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd ||
+ op == Add || op == Mul || op == Equal || op == NotEqual; }
+ static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; }
+ static bool isCompareOp(Value op) { return Equal <= op && op <= In; }
+
+ static Value AssignmentToBinaryOp(Value op)
+ {
+ solAssert(isAssignmentOp(op) && op != Assign, "");
+ return Value(op + (BitOr - AssignBitOr));
+ }
+
+ static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; }
+ static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; }
+ static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub || op == After; }
+ static bool isCountOp(Value op) { return op == Inc || op == Dec; }
+ static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
+ static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
+ static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
+ static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
+ static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
+ static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
+
+ // @returns a string corresponding to the JS token string
+ // (.e., "<" for the token LT) or NULL if the token doesn't
+ // have a (unique) string (e.g. an IDENTIFIER).
+ static char const* toString(Value tok)
+ {
+ solAssert(tok < NUM_TOKENS, "");
+ return m_string[tok];
+ }
+
+ // @returns the precedence > 0 for binary and compare
+ // operators; returns 0 otherwise.
+ static int precedence(Value tok)
+ {
+ solAssert(tok < NUM_TOKENS, "");
+ return m_precedence[tok];
+ }
+
+ static std::tuple<Token::Value, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal);
+
+private:
+ // @returns -1 on error (invalid digit or number too large)
+ static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end);
+ // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists.
+ static Token::Value keywordByName(std::string const& _name);
+ static char const* const m_name[NUM_TOKENS];
+ static char const* const m_string[NUM_TOKENS];
+ static int8_t const m_precedence[NUM_TOKENS];
+ static char const m_tokenType[NUM_TOKENS];
+};
+
+class ElementaryTypeNameToken
+{
+public:
+ ElementaryTypeNameToken(Token::Value _token, unsigned const& _firstNumber, unsigned const& _secondNumber)
+ {
+ assertDetails(_token, _firstNumber, _secondNumber);
+ }
+
+ unsigned int firstNumber() const { return m_firstNumber; }
+ unsigned int secondNumber() const { return m_secondNumber; }
+ Token::Value token() const { return m_token; }
+ ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type
+ std::string toString(bool const& tokenValue = false) const
+ {
+ std::string name = Token::toString(m_token);
+ if (tokenValue || (firstNumber() == 0 && secondNumber() == 0))
+ return name;
+ //need to set it up this way for fixed types construction in future
+ return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber);
+ }
+
+private:
+ Token::Value m_token;
+ unsigned int m_firstNumber;
+ unsigned int m_secondNumber;
+ /// throws if type is not properly sized
+ void assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second);
+};
+
+}
+}
diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt
new file mode 100644
index 00000000..9a914af8
--- /dev/null
+++ b/solc/CMakeLists.txt
@@ -0,0 +1,27 @@
+aux_source_directory(. SRC_LIST)
+list(REMOVE_ITEM SRC_LIST "./jsonCompiler.cpp")
+
+include_directories(BEFORE ..)
+
+set(EXECUTABLE solc)
+
+file(GLOB HEADERS "*.h")
+add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
+
+eth_use(${EXECUTABLE} REQUIRED Solidity::solidity)
+target_link_libraries(${EXECUTABLE} ${Boost_PROGRAM_OPTIONS_LIBRARIES})
+
+if (APPLE)
+ install(TARGETS ${EXECUTABLE} DESTINATION bin)
+else()
+ eth_install_executable(${EXECUTABLE})
+endif()
+
+if (EMSCRIPTEN)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\"]' -s RESERVED_FUNCTION_POINTERS=20")
+ add_executable(soljson jsonCompiler.cpp ${HEADERS})
+ eth_use(soljson REQUIRED Solidity::solidity)
+else()
+ add_library(soljson jsonCompiler.cpp ${HEADERS})
+ target_link_libraries(soljson solidity)
+endif()
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
new file mode 100644
index 00000000..499b1f7a
--- /dev/null
+++ b/solc/CommandLineInterface.cpp
@@ -0,0 +1,860 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Solidity command line interface.
+ */
+#include "CommandLineInterface.h"
+
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "solidity/BuildInfo.h"
+#include <libdevcore/Common.h>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/CommonIO.h>
+#include <libevmcore/Instruction.h>
+#include <libsolidity/interface/Version.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/ast/ASTPrinter.h>
+#include <libsolidity/ast/ASTJsonConverter.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/interface/GasEstimator.h>
+#include <libsolidity/formal/Why3Translator.h>
+
+using namespace std;
+namespace po = boost::program_options;
+
+namespace dev
+{
+namespace solidity
+{
+
+static string const g_argAbiStr = "abi";
+static string const g_argSolInterfaceStr = "interface";
+static string const g_argSignatureHashes = "hashes";
+static string const g_argGas = "gas";
+static string const g_argAsmStr = "asm";
+static string const g_argAsmJsonStr = "asm-json";
+static string const g_argAstStr = "ast";
+static string const g_argAstJson = "ast-json";
+static string const g_argBinaryStr = "bin";
+static string const g_argRuntimeBinaryStr = "bin-runtime";
+static string const g_argCloneBinaryStr = "clone-bin";
+static string const g_argOpcodesStr = "opcodes";
+static string const g_argNatspecDevStr = "devdoc";
+static string const g_argNatspecUserStr = "userdoc";
+static string const g_argAddStandard = "add-std";
+static string const g_stdinFileName = "<stdin>";
+
+/// Possible arguments to for --combined-json
+static set<string> const g_combinedJsonArgs{
+ "bin",
+ "bin-runtime",
+ "clone-bin",
+ "opcodes",
+ "abi",
+ "interface",
+ "asm",
+ "ast",
+ "userdoc",
+ "devdoc"
+};
+
+static void version()
+{
+ cout <<
+ "solc, the solidity compiler commandline interface" <<
+ endl <<
+ "Version: " <<
+ dev::solidity::VersionString <<
+ endl;
+ exit(0);
+}
+
+static bool needsHumanTargetedStdout(po::variables_map const& _args)
+{
+ if (_args.count(g_argGas))
+ return true;
+ if (_args.count("output-dir"))
+ return false;
+ for (string const& arg: {
+ g_argAbiStr,
+ g_argSolInterfaceStr,
+ g_argSignatureHashes,
+ g_argNatspecUserStr,
+ g_argAstJson,
+ g_argNatspecDevStr,
+ g_argAsmStr,
+ g_argAsmJsonStr,
+ g_argOpcodesStr,
+ g_argBinaryStr,
+ g_argRuntimeBinaryStr,
+ g_argCloneBinaryStr,
+ string("formal")
+ })
+ if (_args.count(arg))
+ return true;
+ return false;
+}
+
+void CommandLineInterface::handleBinary(string const& _contract)
+{
+ if (m_args.count(g_argBinaryStr))
+ {
+ if (m_args.count("output-dir"))
+ createFile(_contract + ".bin", m_compiler->object(_contract).toHex());
+ else
+ {
+ cout << "Binary: " << endl;
+ cout << m_compiler->object(_contract).toHex() << endl;
+ }
+ }
+ if (m_args.count(g_argCloneBinaryStr))
+ {
+ if (m_args.count("output-dir"))
+ createFile(_contract + ".clone_bin", m_compiler->cloneObject(_contract).toHex());
+ else
+ {
+ cout << "Clone Binary: " << endl;
+ cout << m_compiler->cloneObject(_contract).toHex() << endl;
+ }
+ }
+ if (m_args.count(g_argRuntimeBinaryStr))
+ {
+ if (m_args.count("output-dir"))
+ createFile(_contract + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex());
+ else
+ {
+ cout << "Binary of the runtime part: " << endl;
+ cout << m_compiler->runtimeObject(_contract).toHex() << endl;
+ }
+ }
+}
+
+void CommandLineInterface::handleOpcode(string const& _contract)
+{
+ if (m_args.count("output-dir"))
+ createFile(_contract + ".opcode", eth::disassemble(m_compiler->object(_contract).bytecode));
+ else
+ {
+ cout << "Opcodes: " << endl;
+ cout << eth::disassemble(m_compiler->object(_contract).bytecode);
+ cout << endl;
+ }
+}
+
+void CommandLineInterface::handleBytecode(string const& _contract)
+{
+ if (m_args.count(g_argOpcodesStr))
+ handleOpcode(_contract);
+ if (m_args.count(g_argBinaryStr) || m_args.count(g_argCloneBinaryStr) || m_args.count(g_argRuntimeBinaryStr))
+ handleBinary(_contract);
+}
+
+void CommandLineInterface::handleSignatureHashes(string const& _contract)
+{
+ if (!m_args.count(g_argSignatureHashes))
+ return;
+
+ string out;
+ for (auto const& it: m_compiler->contractDefinition(_contract).interfaceFunctions())
+ out += toHex(it.first.ref()) + ": " + it.second->externalSignature() + "\n";
+
+ if (m_args.count("output-dir"))
+ createFile(_contract + ".signatures", out);
+ else
+ cout << "Function signatures: " << endl << out;
+}
+
+void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
+{
+ std::string argName;
+ std::string suffix;
+ std::string title;
+ switch(_type)
+ {
+ case DocumentationType::ABIInterface:
+ argName = g_argAbiStr;
+ suffix = ".abi";
+ title = "Contract JSON ABI";
+ break;
+ case DocumentationType::ABISolidityInterface:
+ argName = g_argSolInterfaceStr;
+ suffix = "_interface.sol";
+ title = "Contract Solidity ABI";
+ break;
+ case DocumentationType::NatspecUser:
+ argName = g_argNatspecUserStr;
+ suffix = ".docuser";
+ title = "User Documentation";
+ break;
+ case DocumentationType::NatspecDev:
+ argName = g_argNatspecDevStr;
+ suffix = ".docdev";
+ title = "Developer Documentation";
+ break;
+ default:
+ // should never happen
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type"));
+ }
+
+ if (m_args.count(argName))
+ {
+ if (m_args.count("output-dir"))
+ createFile(_contract + suffix, m_compiler->metadata(_contract, _type));
+ else
+ {
+ cout << title << endl;
+ cout << m_compiler->metadata(_contract, _type) << endl;
+ }
+
+ }
+}
+
+void CommandLineInterface::handleGasEstimation(string const& _contract)
+{
+ eth::EVMSchedule schedule; // TODO: make it relevant to the SealEngine/EnvInfo.
+ using Gas = GasEstimator::GasConsumption;
+ if (!m_compiler->assemblyItems(_contract) && !m_compiler->runtimeAssemblyItems(_contract))
+ return;
+ cout << "Gas estimation:" << endl;
+ if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
+ {
+ Gas gas = GasEstimator::functionalEstimation(*items);
+ u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
+ cout << "construction:" << endl;
+ cout << " " << gas << " + " << (bytecodeSize * schedule.createDataGas) << " = ";
+ gas += bytecodeSize * schedule.createDataGas;
+ cout << gas << endl;
+ }
+ if (eth::AssemblyItems const* items = m_compiler->runtimeAssemblyItems(_contract))
+ {
+ ContractDefinition const& contract = m_compiler->contractDefinition(_contract);
+ cout << "external:" << endl;
+ for (auto it: contract.interfaceFunctions())
+ {
+ string sig = it.second->externalSignature();
+ GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, sig);
+ cout << " " << sig << ":\t" << gas << endl;
+ }
+ if (contract.fallbackFunction())
+ {
+ GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, "INVALID");
+ cout << " fallback:\t" << gas << endl;
+ }
+ cout << "internal:" << endl;
+ for (auto const& it: contract.definedFunctions())
+ {
+ if (it->isPartOfExternalInterface() || it->isConstructor())
+ continue;
+ size_t entry = m_compiler->functionEntryPoint(_contract, *it);
+ GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
+ if (entry > 0)
+ gas = GasEstimator::functionalEstimation(*items, entry, *it);
+ FunctionType type(*it);
+ cout << " " << it->name() << "(";
+ auto paramTypes = type.parameterTypes();
+ for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
+ cout << (*it)->toString() << (it + 1 == paramTypes.end() ? "" : ",");
+ cout << "):\t" << gas << endl;
+ }
+ }
+}
+
+void CommandLineInterface::handleFormal()
+{
+ if (!m_args.count("formal"))
+ return;
+
+ if (m_args.count("output-dir"))
+ createFile("solidity.mlw", m_compiler->formalTranslation());
+ else
+ cout << "Formal version:" << endl << m_compiler->formalTranslation() << endl;
+}
+
+void CommandLineInterface::readInputFilesAndConfigureRemappings()
+{
+ if (!m_args.count("input-file"))
+ {
+ string s;
+ while (!cin.eof())
+ {
+ getline(cin, s);
+ m_sourceCodes[g_stdinFileName].append(s + '\n');
+ }
+ }
+ else
+ for (string const& infile: m_args["input-file"].as<vector<string>>())
+ {
+ auto eq = find(infile.begin(), infile.end(), '=');
+ if (eq != infile.end())
+ {
+ string target(eq + 1, infile.end());
+ m_remappings.push_back(make_pair(string(infile.begin(), eq), target));
+ m_allowedDirectories.push_back(boost::filesystem::path(target).remove_filename());
+ }
+ else
+ {
+ auto path = boost::filesystem::path(infile);
+ if (!boost::filesystem::exists(path))
+ {
+ cerr << "Skipping non existant input file \"" << infile << "\"" << endl;
+ continue;
+ }
+
+ if (!boost::filesystem::is_regular_file(path))
+ {
+ cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl;
+ continue;
+ }
+
+ m_sourceCodes[path.string()] = dev::contentsString(path.string());
+ m_allowedDirectories.push_back(boost::filesystem::canonical(path).remove_filename());
+ }
+ }
+ // Add empty remapping to try the path itself.
+ m_remappings.push_back(make_pair(string(), string()));
+}
+
+bool CommandLineInterface::parseLibraryOption(string const& _input)
+{
+ namespace fs = boost::filesystem;
+ string data = fs::is_regular_file(_input) ? contentsString(_input) : _input;
+
+ vector<string> libraries;
+ boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on);
+ for (string const& lib: libraries)
+ if (!lib.empty())
+ {
+ auto colon = lib.find(':');
+ if (colon == string::npos)
+ {
+ cerr << "Colon separator missing in library address specifier \"" << lib << "\"" << endl;
+ return false;
+ }
+ string libName(lib.begin(), lib.begin() + colon);
+ string addrString(lib.begin() + colon + 1, lib.end());
+ boost::trim(libName);
+ boost::trim(addrString);
+ bytes binAddr = fromHex(addrString);
+ h160 address(binAddr, h160::AlignRight);
+ if (binAddr.size() > 20 || address == h160())
+ {
+ cerr << "Invalid address for library \"" << libName << "\": " << addrString << endl;
+ return false;
+ }
+ m_libraries[libName] = address;
+ }
+
+ return true;
+}
+
+void CommandLineInterface::createFile(string const& _fileName, string const& _data)
+{
+ namespace fs = boost::filesystem;
+ // create directory if not existent
+ fs::path p(m_args.at("output-dir").as<string>());
+ fs::create_directories(p);
+ string pathName = (p / _fileName).string();
+ ofstream outFile(pathName);
+ outFile << _data;
+ if (!outFile)
+ BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + pathName));
+}
+
+bool CommandLineInterface::parseArguments(int _argc, char** _argv)
+{
+ // Declare the supported options.
+ po::options_description desc(
+ R"(solc, the Solidity commandline compiler.
+Usage: solc [options] [input_file...]
+Compiles the given Solidity input files (or the standard input if none given) and
+outputs the components specified in the options at standard output or in files in
+the output directory, if specified.
+Example: solc --bin -o /tmp/solcoutput contract.sol
+
+Allowed options)",
+ po::options_description::m_default_line_length,
+ po::options_description::m_default_line_length - 23);
+ desc.add_options()
+ ("help", "Show help message and exit.")
+ ("version", "Show version and exit.")
+ ("optimize", "Enable bytecode optimizer.")
+ (
+ "optimize-runs",
+ po::value<unsigned>()->value_name("n")->default_value(200),
+ "Estimated number of contract runs for optimizer tuning."
+ )
+ (g_argAddStandard.c_str(), "Add standard contracts.")
+ (
+ "libraries",
+ po::value<vector<string>>()->value_name("libs"),
+ "Direct string or file containing library addresses. Syntax: "
+ "<libraryName>: <address> [, or whitespace] ...\n"
+ "Address is interpreted as a hex string optionally prefixed by 0x."
+ )
+ (
+ "output-dir,o",
+ po::value<string>()->value_name("path"),
+ "If given, creates one file per component and contract/file at the specified directory."
+ )
+ (
+ "combined-json",
+ po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
+ "Output a single json document containing the specified information."
+ )
+ (g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
+ (
+ "link",
+ "Switch to linker mode, ignoring all options apart from --libraries "
+ "and modify binaries in place."
+ );
+ po::options_description outputComponents("Output Components");
+ outputComponents.add_options()
+ (g_argAstStr.c_str(), "AST of all source files.")
+ (g_argAstJson.c_str(), "AST of all source files in JSON format.")
+ (g_argAsmStr.c_str(), "EVM assembly of the contracts.")
+ (g_argAsmJsonStr.c_str(), "EVM assembly of the contracts in JSON format.")
+ (g_argOpcodesStr.c_str(), "Opcodes of the contracts.")
+ (g_argBinaryStr.c_str(), "Binary of the contracts in hex.")
+ (g_argRuntimeBinaryStr.c_str(), "Binary of the runtime part of the contracts in hex.")
+ (g_argCloneBinaryStr.c_str(), "Binary of the clone contracts in hex.")
+ (g_argAbiStr.c_str(), "ABI specification of the contracts.")
+ (g_argSolInterfaceStr.c_str(), "Solidity interface of the contracts.")
+ (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.")
+ (g_argNatspecUserStr.c_str(), "Natspec user documentation of all contracts.")
+ (g_argNatspecDevStr.c_str(), "Natspec developer documentation of all contracts.")
+ ("formal", "Translated source suitable for formal analysis.");
+ desc.add(outputComponents);
+
+ po::options_description allOptions = desc;
+ allOptions.add_options()("input-file", po::value<vector<string>>(), "input file");
+
+ // All positional options should be interpreted as input files
+ po::positional_options_description filesPositions;
+ filesPositions.add("input-file", -1);
+
+ // parse the compiler arguments
+ try
+ {
+ po::command_line_parser cmdLineParser(_argc, _argv);
+ cmdLineParser.options(allOptions).positional(filesPositions).allow_unregistered();
+ po::store(cmdLineParser.run(), m_args);
+ }
+ catch (po::error const& _exception)
+ {
+ cerr << _exception.what() << endl;
+ return false;
+ }
+
+ if (m_args.count("help"))
+ {
+ cout << desc;
+ return false;
+ }
+
+ if (m_args.count("version"))
+ {
+ version();
+ return false;
+ }
+
+ if (m_args.count("combined-json"))
+ {
+ vector<string> requests;
+ for (string const& item: boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(",")))
+ if (!g_combinedJsonArgs.count(item))
+ {
+ cerr << "Invalid option to --combined-json: " << item << endl;
+ return false;
+ }
+ }
+ po::notify(m_args);
+
+ return true;
+}
+
+bool CommandLineInterface::processInput()
+{
+ readInputFilesAndConfigureRemappings();
+
+ if (m_args.count("libraries"))
+ for (string const& library: m_args["libraries"].as<vector<string>>())
+ if (!parseLibraryOption(library))
+ return false;
+
+ if (m_args.count("link"))
+ {
+ // switch to linker mode
+ m_onlyLink = true;
+ return link();
+ }
+
+ function<pair<string,string>(string const&)> fileReader = [this](string const& _path)
+ {
+ // Try to find the longest prefix match in all remappings. At the end, there will bean
+ // empty remapping so that we also try the path itself, but any file should be either
+ // in (a subdirectory of) the directory of an explicit source or a remapping target.
+ int errorLevel = 0;
+ size_t longestPrefix = 0;
+ string bestMatchPath;
+ for (auto const& redir: m_remappings)
+ {
+ auto const& virt = redir.first;
+ if (longestPrefix > 0 && virt.length() <= longestPrefix)
+ continue;
+ if (virt.length() > _path.length() || !std::equal(virt.begin(), virt.end(), _path.begin()))
+ continue;
+ string path = redir.second;
+ path.append(_path.begin() + virt.length(), _path.end());
+ auto boostPath = boost::filesystem::path(path);
+ if (!boost::filesystem::exists(boostPath))
+ {
+ errorLevel = max(errorLevel, 0);
+ continue;
+ }
+ boostPath = boost::filesystem::canonical(boostPath);
+ bool isAllowed = false;
+ for (auto const& dir: m_allowedDirectories)
+ {
+ // If dir is a prefix of boostPath, we are fine.
+ if (
+ std::distance(dir.begin(), dir.end()) <= std::distance(boostPath.begin(), boostPath.end()) &&
+ std::equal(dir.begin(), dir.end(), boostPath.begin())
+ )
+ {
+ isAllowed = true;
+ break;
+ }
+ }
+ if (!isAllowed)
+ errorLevel = max(errorLevel, 2);
+ else if (!boost::filesystem::is_regular_file(boostPath))
+ errorLevel = max(errorLevel, 1);
+ else
+ {
+ longestPrefix = virt.length();
+ bestMatchPath = path;
+ }
+ }
+ if (!bestMatchPath.empty())
+ return make_pair(m_sourceCodes[bestMatchPath] = dev::contentsString(bestMatchPath), string());
+ if (errorLevel == 0)
+ return make_pair(string(), string("File not found."));
+ else if (errorLevel == 1)
+ return make_pair(string(), string("Not a valid file."));
+ else
+ return make_pair(string(), string("File outside of allowed directories."));
+ };
+
+ m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader));
+ try
+ {
+ for (auto const& sourceCode: m_sourceCodes)
+ m_compiler->addSource(sourceCode.first, sourceCode.second);
+ // TODO: Perhaps we should not compile unless requested
+ bool optimize = m_args.count("optimize") > 0;
+ unsigned runs = m_args["optimize-runs"].as<unsigned>();
+ bool successful = m_compiler->compile(optimize, runs);
+ if (successful)
+ m_compiler->link(m_libraries);
+
+ if (successful && m_args.count("formal"))
+ if (!m_compiler->prepareFormalAnalysis())
+ successful = false;
+
+ for (auto const& error: m_compiler->errors())
+ SourceReferenceFormatter::printExceptionInformation(
+ cerr,
+ *error,
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error", *m_compiler
+ );
+
+ if (!successful)
+ return false;
+ }
+ catch (CompilerError const& _exception)
+ {
+ SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", *m_compiler);
+ return false;
+ }
+ catch (InternalCompilerError const& _exception)
+ {
+ cerr << "Internal compiler error during compilation:" << endl
+ << boost::diagnostic_information(_exception);
+ return false;
+ }
+ catch (Error const& _error)
+ {
+ if (_error.type() == Error::Type::DocstringParsingError)
+ cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl;
+ else
+ SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), *m_compiler);
+
+ return false;
+ }
+ catch (Exception const& _exception)
+ {
+ cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;
+ return false;
+ }
+ catch (...)
+ {
+ cerr << "Unknown exception during compilation." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+void CommandLineInterface::handleCombinedJSON()
+{
+ if (!m_args.count("combined-json"))
+ return;
+
+ Json::Value output(Json::objectValue);
+
+ output["version"] = ::dev::solidity::VersionString;
+ set<string> requests;
+ boost::split(requests, m_args["combined-json"].as<string>(), boost::is_any_of(","));
+ vector<string> contracts = m_compiler->contractNames();
+
+ if (!contracts.empty())
+ output["contracts"] = Json::Value(Json::objectValue);
+ for (string const& contractName: contracts)
+ {
+ Json::Value contractData(Json::objectValue);
+ if (requests.count("interface"))
+ contractData["interface"] = m_compiler->solidityInterface(contractName);
+ if (requests.count("abi"))
+ contractData["abi"] = m_compiler->interface(contractName);
+ if (requests.count("bin"))
+ contractData["bin"] = m_compiler->object(contractName).toHex();
+ if (requests.count("bin-runtime"))
+ contractData["bin-runtime"] = m_compiler->runtimeObject(contractName).toHex();
+ if (requests.count("clone-bin"))
+ contractData["clone-bin"] = m_compiler->cloneObject(contractName).toHex();
+ if (requests.count("opcodes"))
+ contractData["opcodes"] = eth::disassemble(m_compiler->object(contractName).bytecode);
+ if (requests.count("asm"))
+ {
+ ostringstream unused;
+ contractData["asm"] = m_compiler->streamAssembly(unused, contractName, m_sourceCodes, true);
+ }
+ if (requests.count("devdoc"))
+ contractData["devdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecDev);
+ if (requests.count("userdoc"))
+ contractData["userdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecUser);
+ output["contracts"][contractName] = contractData;
+ }
+
+ if (requests.count("ast"))
+ {
+ output["sources"] = Json::Value(Json::objectValue);
+ for (auto const& sourceCode: m_sourceCodes)
+ {
+ ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
+ output["sources"][sourceCode.first] = Json::Value(Json::objectValue);
+ output["sources"][sourceCode.first]["AST"] = converter.json();
+ }
+ }
+ cout << Json::FastWriter().write(output) << endl;
+}
+
+void CommandLineInterface::handleAst(string const& _argStr)
+{
+ string title;
+
+ if (_argStr == g_argAstStr)
+ title = "Syntax trees:";
+ else if (_argStr == g_argAstJson)
+ title = "JSON AST:";
+ else
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST"));
+
+ // do we need AST output?
+ if (m_args.count(_argStr))
+ {
+ vector<ASTNode const*> asts;
+ for (auto const& sourceCode: m_sourceCodes)
+ asts.push_back(&m_compiler->ast(sourceCode.first));
+ map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
+ if (m_compiler->runtimeAssemblyItems())
+ gasCosts = GasEstimator::breakToStatementLevel(
+ GasEstimator::structuralEstimation(*m_compiler->runtimeAssemblyItems(), asts),
+ asts
+ );
+
+ if (m_args.count("output-dir"))
+ {
+ for (auto const& sourceCode: m_sourceCodes)
+ {
+ stringstream data;
+ string postfix = "";
+ if (_argStr == g_argAstStr)
+ {
+ ASTPrinter printer(m_compiler->ast(sourceCode.first), sourceCode.second);
+ printer.print(data);
+ }
+ else
+ {
+ ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
+ converter.print(data);
+ postfix += "_json";
+ }
+ boost::filesystem::path path(sourceCode.first);
+ createFile(path.filename().string() + postfix + ".ast", data.str());
+ }
+ }
+ else
+ {
+ cout << title << endl << endl;
+ for (auto const& sourceCode: m_sourceCodes)
+ {
+ cout << endl << "======= " << sourceCode.first << " =======" << endl;
+ if (_argStr == g_argAstStr)
+ {
+ ASTPrinter printer(
+ m_compiler->ast(sourceCode.first),
+ sourceCode.second,
+ gasCosts
+ );
+ printer.print(cout);
+ }
+ else
+ {
+ ASTJsonConverter converter(m_compiler->ast(sourceCode.first));
+ converter.print(cout);
+ }
+ }
+ }
+ }
+}
+
+void CommandLineInterface::actOnInput()
+{
+ if (m_onlyLink)
+ writeLinkedFiles();
+ else
+ outputCompilationResults();
+}
+
+bool CommandLineInterface::link()
+{
+ for (auto& src: m_sourceCodes)
+ {
+ auto end = src.second.end();
+ for (auto it = src.second.begin(); it != end;)
+ {
+ while (it != end && *it != '_') ++it;
+ auto insertStart = it;
+ while (it != end && *it == '_') ++it;
+ auto nameStart = it;
+ while (it != end && *it != '_') ++it;
+ auto nameEnd = it;
+ while (it != end && *it == '_') ++it;
+ auto insertEnd = it;
+
+ if (insertStart == end)
+ break;
+
+ if (insertEnd - insertStart != 40)
+ {
+ cerr << "Error in binary object file " << src.first << " at position " << (insertStart - src.second.begin()) << endl;
+ return false;
+ }
+
+ string name(nameStart, nameEnd);
+ if (m_libraries.count(name))
+ {
+ string hexStr(toHex(m_libraries.at(name).asBytes()));
+ copy(hexStr.begin(), hexStr.end(), insertStart);
+ }
+ else
+ cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl;
+ }
+ }
+ return true;
+}
+
+void CommandLineInterface::writeLinkedFiles()
+{
+ for (auto const& src: m_sourceCodes)
+ if (src.first == g_stdinFileName)
+ cout << src.second << endl;
+ else
+ writeFile(src.first, src.second);
+}
+
+void CommandLineInterface::outputCompilationResults()
+{
+ handleCombinedJSON();
+
+ // do we need AST output?
+ handleAst(g_argAstStr);
+ handleAst(g_argAstJson);
+
+ vector<string> contracts = m_compiler->contractNames();
+ for (string const& contract: contracts)
+ {
+ if (needsHumanTargetedStdout(m_args))
+ cout << endl << "======= " << contract << " =======" << endl;
+
+ // do we need EVM assembly?
+ if (m_args.count(g_argAsmStr) || m_args.count(g_argAsmJsonStr))
+ {
+ if (m_args.count("output-dir"))
+ {
+ stringstream data;
+ m_compiler->streamAssembly(data, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
+ createFile(contract + (m_args.count(g_argAsmJsonStr) ? "_evm.json" : ".evm"), data.str());
+ }
+ else
+ {
+ cout << "EVM assembly:" << endl;
+ m_compiler->streamAssembly(cout, contract, m_sourceCodes, m_args.count(g_argAsmJsonStr));
+ }
+ }
+
+ if (m_args.count(g_argGas))
+ handleGasEstimation(contract);
+
+ handleBytecode(contract);
+ handleSignatureHashes(contract);
+ handleMeta(DocumentationType::ABIInterface, contract);
+ handleMeta(DocumentationType::ABISolidityInterface, contract);
+ handleMeta(DocumentationType::NatspecDev, contract);
+ handleMeta(DocumentationType::NatspecUser, contract);
+ } // end of contracts iteration
+
+ handleFormal();
+}
+
+}
+}
diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h
new file mode 100644
index 00000000..d288b5c1
--- /dev/null
+++ b/solc/CommandLineInterface.h
@@ -0,0 +1,93 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2014
+ * Solidity command line interface.
+ */
+#pragma once
+
+#include <libsolidity/interface/CompilerStack.h>
+#include <memory>
+#include <boost/program_options.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace dev
+{
+namespace solidity
+{
+
+//forward declaration
+enum class DocumentationType: uint8_t;
+
+class CommandLineInterface
+{
+public:
+ CommandLineInterface() {}
+
+ /// Parse command line arguments and return false if we should not continue
+ bool parseArguments(int _argc, char** _argv);
+ /// Parse the files and create source code objects
+ bool processInput();
+ /// Perform actions on the input depending on provided compiler arguments
+ void actOnInput();
+
+private:
+ bool link();
+ void writeLinkedFiles();
+
+ void outputCompilationResults();
+
+ void handleCombinedJSON();
+ void handleAst(std::string const& _argStr);
+ void handleBinary(std::string const& _contract);
+ void handleOpcode(std::string const& _contract);
+ void handleBytecode(std::string const& _contract);
+ void handleSignatureHashes(std::string const& _contract);
+ void handleMeta(DocumentationType _type, std::string const& _contract);
+ void handleGasEstimation(std::string const& _contract);
+ void handleFormal();
+
+ /// Fills @a m_sourceCodes initially and @a m_redirects.
+ void readInputFilesAndConfigureRemappings();
+ /// Tries to read from the file @a _input or interprets _input literally if that fails.
+ /// It then tries to parse the contents and appends to m_libraries.
+ bool parseLibraryOption(std::string const& _input);
+
+ /// Create a file in the given directory
+ /// @arg _fileName the name of the file
+ /// @arg _data to be written
+ void createFile(std::string const& _fileName, std::string const& _data);
+
+ bool m_onlyLink = false;
+
+ /// 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;
+ /// list of path prefix remappings, e.g. github.com/ethereum -> /usr/local/ethereum
+ std::vector<std::pair<std::string, std::string>> m_remappings;
+ /// list of allowed directories to read files from
+ std::vector<boost::filesystem::path> m_allowedDirectories;
+ /// map of library names to addresses
+ std::map<std::string, h160> m_libraries;
+ /// Solidity compiler stack
+ std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
+};
+
+}
+}
diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp
new file mode 100644
index 00000000..b5efa94d
--- /dev/null
+++ b/solc/jsonCompiler.cpp
@@ -0,0 +1,275 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * JSON interface for the solidity compiler to be used from Javascript.
+ */
+
+#include <string>
+#include <iostream>
+#include <json/json.h>
+#include <libdevcore/Common.h>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/CommonIO.h>
+#include <libevmcore/Instruction.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/ast/ASTPrinter.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+#include <libsolidity/ast/ASTJsonConverter.h>
+#include <libsolidity/interface/Version.h>
+
+using namespace std;
+using namespace dev;
+using namespace solidity;
+
+extern "C" {
+/// Callback used to retrieve additional source files. "Returns" two pointers that should be
+/// heap-allocated and are free'd by the caller.
+typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error);
+}
+
+string formatError(Exception const& _exception, string const& _name, CompilerStack const& _compiler)
+{
+ ostringstream errorOutput;
+ SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler);
+ return errorOutput.str();
+}
+
+Json::Value functionHashes(ContractDefinition const& _contract)
+{
+ Json::Value functionHashes(Json::objectValue);
+ for (auto const& it: _contract.interfaceFunctions())
+ functionHashes[it.second->externalSignature()] = toHex(it.first.ref());
+ return functionHashes;
+}
+
+Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
+{
+ if (_gas.isInfinite || _gas.value > std::numeric_limits<Json::LargestUInt>::max())
+ return Json::Value(Json::nullValue);
+ else
+ return Json::Value(Json::LargestUInt(_gas.value));
+}
+
+Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
+{
+ eth::EVMSchedule schedule;
+ Json::Value gasEstimates(Json::objectValue);
+ using Gas = GasEstimator::GasConsumption;
+ if (!_compiler.assemblyItems(_contract) && !_compiler.runtimeAssemblyItems(_contract))
+ return gasEstimates;
+ if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract))
+ {
+ Gas gas = GasEstimator::functionalEstimation(*items);
+ u256 bytecodeSize(_compiler.runtimeObject(_contract).bytecode.size());
+ Json::Value creationGas(Json::arrayValue);
+ creationGas[0] = gasToJson(gas);
+ creationGas[1] = gasToJson(bytecodeSize * schedule.createDataGas);
+ gasEstimates["creation"] = creationGas;
+ }
+ if (eth::AssemblyItems const* items = _compiler.runtimeAssemblyItems(_contract))
+ {
+ ContractDefinition const& contract = _compiler.contractDefinition(_contract);
+ Json::Value externalFunctions(Json::objectValue);
+ for (auto it: contract.interfaceFunctions())
+ {
+ string sig = it.second->externalSignature();
+ externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
+ }
+ if (contract.fallbackFunction())
+ externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
+ gasEstimates["external"] = externalFunctions;
+ Json::Value internalFunctions(Json::objectValue);
+ for (auto const& it: contract.definedFunctions())
+ {
+ if (it->isPartOfExternalInterface() || it->isConstructor())
+ continue;
+ size_t entry = _compiler.functionEntryPoint(_contract, *it);
+ GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
+ if (entry > 0)
+ gas = GasEstimator::functionalEstimation(*items, entry, *it);
+ FunctionType type(*it);
+ string sig = it->name() + "(";
+ auto paramTypes = type.parameterTypes();
+ for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
+ sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
+ sig += ")";
+ internalFunctions[sig] = gasToJson(gas);
+ }
+ gasEstimates["internal"] = internalFunctions;
+ }
+ return gasEstimates;
+}
+
+string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)
+{
+ Json::Value output(Json::objectValue);
+ Json::Value errors(Json::arrayValue);
+ CompilerStack::ReadFileCallback readCallback;
+ if (_readCallback)
+ {
+ readCallback = [=](string const& _path) -> pair<string, string>
+ {
+ char* contents_c = nullptr;
+ char* error_c = nullptr;
+ _readCallback(_path.c_str(), &contents_c, &error_c);
+ string contents;
+ string error;
+ if (!contents_c && !error_c)
+ error = "File not found.";
+ if (contents_c)
+ {
+ contents = string(contents_c);
+ free(contents_c);
+ }
+ if (error_c)
+ {
+ error = string(error_c);
+ free(error_c);
+ }
+ return make_pair(move(contents), move(error));
+ };
+ }
+ CompilerStack compiler(true, readCallback);
+ bool success = false;
+ try
+ {
+ compiler.addSources(_sources);
+ bool succ = compiler.compile(_optimize);
+ for (auto const& error: compiler.errors())
+ {
+ auto err = dynamic_pointer_cast<Error const>(error);
+ errors.append(formatError(
+ *error,
+ (err->type() == Error::Type::Warning) ? "Warning" : "Error",
+ compiler
+ ));
+ }
+ success = succ; // keep success false on exception
+ }
+ catch (Error const& error)
+ {
+ errors.append(formatError(error, error.typeName(), compiler));
+ }
+ catch (CompilerError const& exception)
+ {
+ errors.append(formatError(exception, "Compiler error", compiler));
+ }
+ catch (InternalCompilerError const& exception)
+ {
+ errors.append(formatError(exception, "Internal compiler error", compiler));
+ }
+ catch (Exception const& exception)
+ {
+ errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
+ }
+ catch (...)
+ {
+ errors.append("Unknown exception during compilation.");
+ }
+
+ if (errors.size() > 0)
+ output["errors"] = errors;
+
+ if (success)
+ {
+ output["contracts"] = Json::Value(Json::objectValue);
+ for (string const& contractName: compiler.contractNames())
+ {
+ Json::Value contractData(Json::objectValue);
+ contractData["solidity_interface"] = compiler.solidityInterface(contractName);
+ contractData["interface"] = compiler.interface(contractName);
+ contractData["bytecode"] = compiler.object(contractName).toHex();
+ contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
+ contractData["opcodes"] = eth::disassemble(compiler.object(contractName).bytecode);
+ contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
+ contractData["gasEstimates"] = estimateGas(compiler, contractName);
+ ostringstream unused;
+ contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true);
+ output["contracts"][contractName] = contractData;
+ }
+
+ output["sources"] = Json::Value(Json::objectValue);
+ for (auto const& source: _sources)
+ {
+ output["sources"][source.first] = Json::Value(Json::objectValue);
+ output["sources"][source.first]["AST"] = ASTJsonConverter(compiler.ast(source.first)).json();
+ }
+ }
+
+ return Json::FastWriter().write(output);
+}
+
+string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr)
+{
+ Json::Reader reader;
+ Json::Value input;
+ if (!reader.parse(_input, input, false))
+ {
+ Json::Value errors(Json::arrayValue);
+ errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages());
+ Json::Value output(Json::objectValue);
+ output["errors"] = errors;
+ return Json::FastWriter().write(output);
+ }
+ else
+ {
+ StringMap sources;
+ Json::Value jsonSources = input["sources"];
+ if (jsonSources.isObject())
+ for (auto const& sourceName: jsonSources.getMemberNames())
+ sources[sourceName] = jsonSources[sourceName].asString();
+ return compile(sources, _optimize, _readCallback);
+ }
+}
+
+string compileSingle(string const& _input, bool _optimize)
+{
+ StringMap sources;
+ sources[""] = _input;
+ return compile(sources, _optimize, nullptr);
+}
+
+static string s_outputBuffer;
+
+extern "C"
+{
+extern char const* version()
+{
+ return VersionString.c_str();
+}
+extern char const* compileJSON(char const* _input, bool _optimize)
+{
+ s_outputBuffer = compileSingle(_input, _optimize);
+ return s_outputBuffer.c_str();
+}
+extern char const* compileJSONMulti(char const* _input, bool _optimize)
+{
+ s_outputBuffer = compileMulti(_input, _optimize);
+ return s_outputBuffer.c_str();
+}
+extern char const* compileJSONCallback(char const* _input, bool _optimize, CStyleReadFileCallback _readCallback)
+{
+ s_outputBuffer = compileMulti(_input, _optimize, _readCallback);
+ return s_outputBuffer.c_str();
+}
+}
diff --git a/solc/main.cpp b/solc/main.cpp
new file mode 100644
index 00000000..eaada1c4
--- /dev/null
+++ b/solc/main.cpp
@@ -0,0 +1,47 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity commandline compiler.
+ */
+
+#include "CommandLineInterface.h"
+#include <iostream>
+#include <boost/exception/all.hpp>
+
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ dev::solidity::CommandLineInterface cli;
+ if (!cli.parseArguments(argc, argv))
+ return 1;
+ if (!cli.processInput())
+ return 1;
+ try
+ {
+ cli.actOnInput();
+ }
+ catch (boost::exception const& _exception)
+ {
+ cerr << "Exception during output generation: " << boost::diagnostic_information(_exception) << endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 00000000..c72d31d3
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,53 @@
+cmake_policy(SET CMP0015 NEW)
+
+aux_source_directory(. SRC_LIST)
+
+get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
+
+macro (add_sources)
+ file (RELATIVE_PATH _relPath ${TESTS_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
+ foreach (_src ${ARGN})
+ if (_relPath)
+ list (APPEND SRC "${_relPath}/${_src}")
+ else()
+ list (APPEND SRC "${_src}")
+ endif()
+ endforeach()
+ if (_relPath)
+ # propagate SRCS to parent directory
+ set (SRC ${SRC} PARENT_SCOPE)
+ endif()
+endmacro()
+
+add_subdirectory(contracts)
+add_subdirectory(libsolidity)
+
+set(SRC_LIST ${SRC_LIST} ${SRC})
+
+# search for test names and create ctest tests
+enable_testing()
+foreach(file ${SRC_LIST})
+ file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${file} test_list_raw REGEX "BOOST_.*TEST_(SUITE|CASE)")
+ set(TestSuite "DEFAULT")
+ foreach(test_raw ${test_list_raw})
+ string(REGEX REPLACE ".*TEST_(SUITE|CASE)\\(([^ ,\\)]*).*" "\\1 \\2" test ${test_raw})
+ if(test MATCHES "^SUITE .*")
+ string(SUBSTRING ${test} 6 -1 TestSuite)
+ elseif(test MATCHES "^CASE .*")
+ string(SUBSTRING ${test} 5 -1 TestCase)
+ add_test(NAME ${TestSuite}/${TestCase} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND test -t ${TestSuite}/${TestCase})
+ endif(test MATCHES "^SUITE .*")
+ endforeach(test_raw)
+endforeach(file)
+
+file(GLOB HEADERS "*.h")
+set(EXECUTABLE soltest)
+add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
+
+eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Eth::ethereum)
+
+include_directories(BEFORE ..)
+target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
+
+enable_testing()
+set(CTEST_OUTPUT_ON_FAILURE TRUE)
diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp
new file mode 100644
index 00000000..79242f83
--- /dev/null
+++ b/test/TestHelper.cpp
@@ -0,0 +1,59 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file TestHelper.cpp
+ * @author Marko Simovic <markobarko@gmail.com>
+ * @date 2014
+ */
+
+#include "TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace test
+{
+
+namespace
+{
+ Listener* g_listener;
+}
+
+void Listener::registerListener(Listener& _listener)
+{
+ g_listener = &_listener;
+}
+
+void Listener::notifySuiteStarted(std::string const& _name)
+{
+ if (g_listener)
+ g_listener->suiteStarted(_name);
+}
+
+void Listener::notifyTestStarted(std::string const& _name)
+{
+ if (g_listener)
+ g_listener->testStarted(_name);
+}
+
+void Listener::notifyTestFinished()
+{
+ if (g_listener)
+ g_listener->testFinished();
+}
+
+} } // namespaces
diff --git a/test/TestHelper.h b/test/TestHelper.h
new file mode 100644
index 00000000..96678007
--- /dev/null
+++ b/test/TestHelper.h
@@ -0,0 +1,133 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file TestHelper.h
+ * @author Marko Simovic <markobarko@gmail.com>
+ * @date 2014
+ */
+
+#pragma once
+
+#include <functional>
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/version.hpp>
+
+namespace dev
+{
+namespace test
+{
+
+#if (BOOST_VERSION >= 105900)
+#define ETH_BOOST_CHECK_IMPL(_message, _requireOrCheck) BOOST_TEST_TOOL_DIRECT_IMPL( \
+ false, \
+ _requireOrCheck, \
+ _message \
+ )
+#else
+#define ETH_BOOST_CHECK_IMPL(_message, _requireOrCheck) BOOST_CHECK_IMPL( \
+ false, \
+ _message, \
+ _requireOrCheck, \
+ CHECK_MSG \
+ )
+#endif
+
+/// Make sure that no Exception is thrown during testing. If one is thrown show its info and fail the test.
+/// Our version of BOOST_REQUIRE_NO_THROW()
+/// @param _statement The statement for which to make sure no exceptions are thrown
+/// @param _message A message to act as a prefix to the expression's error information
+#define ETH_TEST_REQUIRE_NO_THROW(_statement, _message) \
+ do \
+ { \
+ try \
+ { \
+ BOOST_TEST_PASSPOINT(); \
+ _statement; \
+ } \
+ catch (boost::exception const& _e) \
+ { \
+ auto msg = std::string(_message " due to an exception thrown by " \
+ BOOST_STRINGIZE(_statement) "\n") + boost::diagnostic_information(_e); \
+ ETH_BOOST_CHECK_IMPL(msg, REQUIRE); \
+ } \
+ catch (...) \
+ { \
+ ETH_BOOST_CHECK_IMPL( \
+ "Unknown exception thrown by " BOOST_STRINGIZE(_statement), \
+ REQUIRE \
+ ); \
+ } \
+ } \
+ while (0)
+
+/// Check if an Exception is thrown during testing. If one is thrown show its info and continue the test
+/// Our version of BOOST_CHECK_NO_THROW()
+/// @param _statement The statement for which to make sure no exceptions are thrown
+/// @param _message A message to act as a prefix to the expression's error information
+#define ETH_TEST_CHECK_NO_THROW(_statement, _message) \
+ do \
+ { \
+ try \
+ { \
+ BOOST_TEST_PASSPOINT(); \
+ _statement; \
+ } \
+ catch (boost::exception const& _e) \
+ { \
+ auto msg = std::string(_message " due to an exception thrown by " \
+ BOOST_STRINGIZE(_statement) "\n") + boost::diagnostic_information(_e); \
+ ETH_BOOST_CHECK_IMPL(msg, CHECK); \
+ } \
+ catch (...) \
+ { \
+ ETH_BOOST_CHECK_IMPL( \
+ "Unknown exception thrown by " BOOST_STRINGIZE(_statement), \
+ CHECK \
+ ); \
+ } \
+ } \
+ while (0)
+
+/// Allows observing test execution process.
+/// This class also provides methods for registering and notifying the listener
+class Listener
+{
+public:
+ virtual ~Listener() = default;
+
+ virtual void suiteStarted(std::string const&) {}
+ virtual void testStarted(std::string const& _name) = 0;
+ virtual void testFinished() = 0;
+
+ static void registerListener(Listener& _listener);
+ static void notifySuiteStarted(std::string const& _name);
+ static void notifyTestStarted(std::string const& _name);
+ static void notifyTestFinished();
+
+ /// Test started/finished notification RAII helper
+ class ExecTimeGuard
+ {
+ public:
+ ExecTimeGuard(std::string const& _testName) { notifyTestStarted(_testName); }
+ ~ExecTimeGuard() { notifyTestFinished(); }
+ ExecTimeGuard(ExecTimeGuard const&) = delete;
+ ExecTimeGuard& operator=(ExecTimeGuard) = delete;
+ };
+};
+
+}
+}
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
new file mode 100644
index 00000000..f3400a31
--- /dev/null
+++ b/test/boostTest.cpp
@@ -0,0 +1,33 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/** @file boostTest.cpp
+ * @author Marko Simovic <markobarko@gmail.com>
+ * @date 2014
+ * Stub for generating main boost.test module.
+ * Original code taken from boost sources.
+ */
+
+#define BOOST_TEST_MODULE EthereumTests
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+//#define BOOST_DISABLE_WIN32 //disables SEH warning
+#include <boost/test/included/unit_test.hpp>
+#pragma GCC diagnostic pop
+
+#include <test/TestHelper.h>
+using namespace boost::unit_test;
+
diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp
new file mode 100644
index 00000000..e13a79e0
--- /dev/null
+++ b/test/contracts/AuctionRegistrar.cpp
@@ -0,0 +1,492 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Tests for a fixed fee registrar contract.
+ */
+
+#include <string>
+#include <tuple>
+#include <boost/test/unit_test.hpp>
+#include <libdevcore/Hash.h>
+#include <libethcore/ABI.h>
+#include <test/libsolidity/solidityExecutionFramework.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+static char const* registrarCode = R"DELIMITER(
+//sol
+
+contract NameRegister {
+ function addr(string _name) constant returns (address o_owner);
+ function name(address _owner) constant returns (string o_name);
+}
+
+contract Registrar is NameRegister {
+ event Changed(string indexed name);
+ event PrimaryChanged(string indexed name, address indexed addr);
+
+ function owner(string _name) constant returns (address o_owner);
+ function addr(string _name) constant returns (address o_address);
+ function subRegistrar(string _name) constant returns (address o_subRegistrar);
+ function content(string _name) constant returns (bytes32 o_content);
+
+ function name(address _owner) constant returns (string o_name);
+}
+
+contract AuctionSystem {
+ event AuctionEnded(string indexed _name, address _winner);
+ event NewBid(string indexed _name, address _bidder, uint _value);
+
+ /// Function that is called once an auction ends.
+ function onAuctionEnd(string _name) internal;
+
+ function bid(string _name, address _bidder, uint _value) internal {
+ var auction = m_auctions[_name];
+ if (auction.endDate > 0 && now > auction.endDate)
+ {
+ AuctionEnded(_name, auction.highestBidder);
+ onAuctionEnd(_name);
+ delete m_auctions[_name];
+ return;
+ }
+ if (msg.value > auction.highestBid)
+ {
+ // new bid on auction
+ auction.secondHighestBid = auction.highestBid;
+ auction.sumOfBids += _value;
+ auction.highestBid = _value;
+ auction.highestBidder = _bidder;
+ auction.endDate = now + c_biddingTime;
+
+ NewBid(_name, _bidder, _value);
+ }
+ }
+
+ uint constant c_biddingTime = 7 days;
+
+ struct Auction {
+ address highestBidder;
+ uint highestBid;
+ uint secondHighestBid;
+ uint sumOfBids;
+ uint endDate;
+ }
+ mapping(string => Auction) m_auctions;
+}
+
+contract GlobalRegistrar is Registrar, AuctionSystem {
+ struct Record {
+ address owner;
+ address primary;
+ address subRegistrar;
+ bytes32 content;
+ uint renewalDate;
+ }
+
+ uint constant c_renewalInterval = 1 years;
+ uint constant c_freeBytes = 12;
+
+ function Registrar() {
+ // TODO: Populate with hall-of-fame.
+ }
+
+ function() {
+ // prevent people from just sending funds to the registrar
+ throw;
+ }
+
+ function onAuctionEnd(string _name) internal {
+ var auction = m_auctions[_name];
+ var record = m_toRecord[_name];
+ if (record.owner != 0)
+ record.owner.send(auction.sumOfBids - auction.highestBid / 100);
+ else
+ auction.highestBidder.send(auction.highestBid - auction.secondHighestBid);
+ record.renewalDate = now + c_renewalInterval;
+ record.owner = auction.highestBidder;
+ Changed(_name);
+ }
+
+ function reserve(string _name) external {
+ if (bytes(_name).length == 0)
+ throw;
+ bool needAuction = requiresAuction(_name);
+ if (needAuction)
+ {
+ if (now < m_toRecord[_name].renewalDate)
+ throw;
+ bid(_name, msg.sender, msg.value);
+ }
+ else
+ {
+ Record record = m_toRecord[_name];
+ if (record.owner != 0)
+ throw;
+ m_toRecord[_name].owner = msg.sender;
+ Changed(_name);
+ }
+ }
+
+ function requiresAuction(string _name) internal returns (bool) {
+ return bytes(_name).length < c_freeBytes;
+ }
+
+ modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _ }
+
+ function transfer(string _name, address _newOwner) onlyrecordowner(_name) {
+ m_toRecord[_name].owner = _newOwner;
+ Changed(_name);
+ }
+
+ function disown(string _name) onlyrecordowner(_name) {
+ if (stringsEqual(m_toName[m_toRecord[_name].primary], _name))
+ {
+ PrimaryChanged(_name, m_toRecord[_name].primary);
+ m_toName[m_toRecord[_name].primary] = "";
+ }
+ delete m_toRecord[_name];
+ Changed(_name);
+ }
+
+ function setAddress(string _name, address _a, bool _primary) onlyrecordowner(_name) {
+ m_toRecord[_name].primary = _a;
+ if (_primary)
+ {
+ PrimaryChanged(_name, _a);
+ m_toName[_a] = _name;
+ }
+ Changed(_name);
+ }
+ function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) {
+ m_toRecord[_name].subRegistrar = _registrar;
+ Changed(_name);
+ }
+ function setContent(string _name, bytes32 _content) onlyrecordowner(_name) {
+ m_toRecord[_name].content = _content;
+ Changed(_name);
+ }
+
+ function stringsEqual(string storage _a, string memory _b) internal returns (bool) {
+ bytes storage a = bytes(_a);
+ bytes memory b = bytes(_b);
+ if (a.length != b.length)
+ return false;
+ // @todo unroll this loop
+ for (uint i = 0; i < a.length; i ++)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+
+ function owner(string _name) constant returns (address) { return m_toRecord[_name].owner; }
+ function addr(string _name) constant returns (address) { return m_toRecord[_name].primary; }
+ function subRegistrar(string _name) constant returns (address) { return m_toRecord[_name].subRegistrar; }
+ function content(string _name) constant returns (bytes32) { return m_toRecord[_name].content; }
+ function name(address _addr) constant returns (string o_name) { return m_toName[_addr]; }
+
+ mapping (address => string) m_toName;
+ mapping (string => Record) m_toRecord;
+}
+)DELIMITER";
+
+static unique_ptr<bytes> s_compiledRegistrar;
+
+class AuctionRegistrarTestFramework: public ExecutionFramework
+{
+protected:
+ void deployRegistrar()
+ {
+ if (!s_compiledRegistrar)
+ {
+ m_optimize = true;
+ m_compiler.reset(false, m_addStandardSources);
+ m_compiler.addSource("", registrarCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
+ s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode));
+ }
+ sendMessage(*s_compiledRegistrar, true);
+ BOOST_REQUIRE(!m_output.empty());
+ }
+
+ using ContractInterface = ExecutionFramework::ContractInterface;
+ class RegistrarInterface: public ContractInterface
+ {
+ public:
+ RegistrarInterface(ExecutionFramework& _framework): ContractInterface(_framework) {}
+ void reserve(string const& _name)
+ {
+ callString("reserve", _name);
+ }
+ u160 owner(string const& _name)
+ {
+ return callStringReturnsAddress("owner", _name);
+ }
+ void setAddress(string const& _name, u160 const& _address, bool _primary)
+ {
+ callStringAddressBool("setAddress", _name, _address, _primary);
+ }
+ u160 addr(string const& _name)
+ {
+ return callStringReturnsAddress("addr", _name);
+ }
+ string name(u160 const& _addr)
+ {
+ return callAddressReturnsString("name", _addr);
+ }
+ void setSubRegistrar(string const& _name, u160 const& _address)
+ {
+ callStringAddress("setSubRegistrar", _name, _address);
+ }
+ u160 subRegistrar(string const& _name)
+ {
+ return callStringReturnsAddress("subRegistrar", _name);
+ }
+ void setContent(string const& _name, h256 const& _content)
+ {
+ callStringBytes32("setContent", _name, _content);
+ }
+ h256 content(string const& _name)
+ {
+ return callStringReturnsBytes32("content", _name);
+ }
+ void transfer(string const& _name, u160 const& _target)
+ {
+ return callStringAddress("transfer", _name, _target);
+ }
+ void disown(string const& _name)
+ {
+ return callString("disown", _name);
+ }
+ };
+
+ u256 const m_biddingTime = u256(7 * 24 * 3600);
+ u256 const m_renewalInterval = u256(365 * 24 * 3600);
+};
+
+}
+
+/// This is a test suite that tests optimised code!
+BOOST_FIXTURE_TEST_SUITE(SolidityAuctionRegistrar, AuctionRegistrarTestFramework)
+
+BOOST_AUTO_TEST_CASE(creation)
+{
+ deployRegistrar();
+}
+
+BOOST_AUTO_TEST_CASE(reserve)
+{
+ // Test that reserving works for long strings
+ deployRegistrar();
+ vector<string> names{"abcabcabcabcabc", "defdefdefdefdef", "ghighighighighighighighighighighighighighighi"};
+ m_sender = Address(0x123);
+
+ RegistrarInterface registrar(*this);
+
+ // should not work
+ registrar.reserve("");
+ BOOST_CHECK_EQUAL(registrar.owner(""), u160(0));
+
+ for (auto const& name: names)
+ {
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(double_reserve_long)
+{
+ // Test that it is not possible to re-reserve from a different address.
+ deployRegistrar();
+ string name = "abcabcabcabcabcabcabcabcabcabca";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+
+ m_sender = Address(0x124);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+}
+
+BOOST_AUTO_TEST_CASE(properties)
+{
+ // Test setting and retrieving the various properties works.
+ deployRegistrar();
+ RegistrarInterface registrar(*this);
+ string names[] = {"abcaeouoeuaoeuaoeu", "defncboagufra,fui", "ghagpyajfbcuajouhaeoi"};
+ size_t addr = 0x9872543;
+ for (string const& name: names)
+ {
+ addr++;
+ size_t sender = addr + 10007;
+ m_sender = Address(sender);
+ // setting by sender works
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
+ registrar.setAddress(name, addr, true);
+ BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
+ registrar.setSubRegistrar(name, addr + 20);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
+ registrar.setContent(name, h256(u256(addr + 90)));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
+
+ // but not by someone else
+ m_sender = Address(h256(addr + 10007 - 1));
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
+ registrar.setAddress(name, addr + 1, true);
+ BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
+ registrar.setSubRegistrar(name, addr + 20 + 1);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
+ registrar.setContent(name, h256(u256(addr + 90 + 1)));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(transfer)
+{
+ deployRegistrar();
+ string name = "abcaoeguaoucaeoduceo";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ registrar.setContent(name, h256(u256(123)));
+ registrar.transfer(name, u160(555));
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(555));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(123)));
+}
+
+BOOST_AUTO_TEST_CASE(disown)
+{
+ deployRegistrar();
+ string name = "abcaoeguaoucaeoduceo";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ registrar.setContent(name, h256(u256(123)));
+ registrar.setAddress(name, u160(124), true);
+ registrar.setSubRegistrar(name, u160(125));
+ BOOST_CHECK_EQUAL(registrar.name(u160(124)), name);
+
+ // someone else tries disowning
+ m_sender = Address(0x128);
+ registrar.disown(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+
+ m_sender = Address(0x123);
+ registrar.disown(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0);
+ BOOST_CHECK_EQUAL(registrar.addr(name), 0);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), 0);
+ BOOST_CHECK_EQUAL(registrar.content(name), h256());
+ BOOST_CHECK_EQUAL(registrar.name(u160(124)), "");
+}
+
+BOOST_AUTO_TEST_CASE(auction_simple)
+{
+ deployRegistrar();
+ string name = "x";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ // initiate auction
+ registrar.setNextValue(8);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0);
+ // "wait" until auction end
+ m_envInfo.setTimestamp(m_envInfo.timestamp() + m_biddingTime + 10);
+ // trigger auction again
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+}
+
+BOOST_AUTO_TEST_CASE(auction_bidding)
+{
+ deployRegistrar();
+ string name = "x";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ // initiate auction
+ registrar.setNextValue(8);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0);
+ // overbid self
+ m_envInfo.setTimestamp(m_biddingTime - 10);
+ registrar.setNextValue(12);
+ registrar.reserve(name);
+ // another bid by someone else
+ m_sender = Address(0x124);
+ m_envInfo.setTimestamp(2 * m_biddingTime - 50);
+ registrar.setNextValue(13);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0);
+ // end auction by first bidder (which is not highest) trying to overbid again (too late)
+ m_sender = Address(0x123);
+ m_envInfo.setTimestamp(4 * m_biddingTime);
+ registrar.setNextValue(20);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x124);
+}
+
+BOOST_AUTO_TEST_CASE(auction_renewal)
+{
+ deployRegistrar();
+ string name = "x";
+ RegistrarInterface registrar(*this);
+ // register name by auction
+ m_sender = Address(0x123);
+ registrar.setNextValue(8);
+ registrar.reserve(name);
+ m_envInfo.setTimestamp(4 * m_biddingTime);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+
+ // try to re-register before interval end
+ m_sender = Address(0x222);
+ registrar.setNextValue(80);
+ m_envInfo.setTimestamp(m_envInfo.timestamp() + m_renewalInterval - 1);
+ registrar.reserve(name);
+ m_envInfo.setTimestamp(m_envInfo.timestamp() + m_biddingTime);
+ // if there is a bug in the renewal logic, this would transfer the ownership to 0x222,
+ // but if there is no bug, this will initiate the auction, albeit with a zero bid
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+
+ m_envInfo.setTimestamp(m_envInfo.timestamp() + 2);
+ registrar.setNextValue(80);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+ m_envInfo.setTimestamp(m_envInfo.timestamp() + m_biddingTime + 2);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x222);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/contracts/CMakeLists.txt b/test/contracts/CMakeLists.txt
new file mode 100644
index 00000000..3ceda13b
--- /dev/null
+++ b/test/contracts/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_policy(SET CMP0015 NEW)
+
+aux_source_directory(. SRCS)
+
+add_sources(${SRCS})
diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp
new file mode 100644
index 00000000..796b3831
--- /dev/null
+++ b/test/contracts/FixedFeeRegistrar.cpp
@@ -0,0 +1,251 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Tests for a fixed fee registrar contract.
+ */
+
+#include <string>
+#include <tuple>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4535) // calling _set_se_translator requires /EHa
+#endif
+#include <boost/test/unit_test.hpp>
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#include <libdevcore/Hash.h>
+#include <test/libsolidity/solidityExecutionFramework.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+static char const* registrarCode = R"DELIMITER(
+//sol FixedFeeRegistrar
+// Simple global registrar with fixed-fee reservations.
+// @authors:
+// Gav Wood <g@ethdev.com>
+
+contract Registrar {
+ event Changed(string indexed name);
+
+ function owner(string _name) constant returns (address o_owner);
+ function addr(string _name) constant returns (address o_address);
+ function subRegistrar(string _name) constant returns (address o_subRegistrar);
+ function content(string _name) constant returns (bytes32 o_content);
+}
+
+contract FixedFeeRegistrar is Registrar {
+ struct Record {
+ address addr;
+ address subRegistrar;
+ bytes32 content;
+ address owner;
+ }
+
+ modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _ }
+
+ function reserve(string _name) {
+ Record rec = m_record(_name);
+ if (rec.owner == 0 && msg.value >= c_fee) {
+ rec.owner = msg.sender;
+ Changed(_name);
+ }
+ }
+ function disown(string _name, address _refund) onlyrecordowner(_name) {
+ delete m_recordData[uint(sha3(_name)) / 8];
+ _refund.send(c_fee);
+ Changed(_name);
+ }
+ function transfer(string _name, address _newOwner) onlyrecordowner(_name) {
+ m_record(_name).owner = _newOwner;
+ Changed(_name);
+ }
+ function setAddr(string _name, address _a) onlyrecordowner(_name) {
+ m_record(_name).addr = _a;
+ Changed(_name);
+ }
+ function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) {
+ m_record(_name).subRegistrar = _registrar;
+ Changed(_name);
+ }
+ function setContent(string _name, bytes32 _content) onlyrecordowner(_name) {
+ m_record(_name).content = _content;
+ Changed(_name);
+ }
+
+ function record(string _name) constant returns (address o_addr, address o_subRegistrar, bytes32 o_content, address o_owner) {
+ Record rec = m_record(_name);
+ o_addr = rec.addr;
+ o_subRegistrar = rec.subRegistrar;
+ o_content = rec.content;
+ o_owner = rec.owner;
+ }
+ function addr(string _name) constant returns (address) { return m_record(_name).addr; }
+ function subRegistrar(string _name) constant returns (address) { return m_record(_name).subRegistrar; }
+ function content(string _name) constant returns (bytes32) { return m_record(_name).content; }
+ function owner(string _name) constant returns (address) { return m_record(_name).owner; }
+
+ Record[2**253] m_recordData;
+ function m_record(string _name) constant internal returns (Record storage o_record) {
+ return m_recordData[uint(sha3(_name)) / 8];
+ }
+ uint constant c_fee = 69 ether;
+}
+)DELIMITER";
+
+static unique_ptr<bytes> s_compiledRegistrar;
+
+class RegistrarTestFramework: public ExecutionFramework
+{
+protected:
+ void deployRegistrar()
+ {
+ if (!s_compiledRegistrar)
+ {
+ m_optimize = true;
+ m_compiler.reset(false, m_addStandardSources);
+ m_compiler.addSource("", registrarCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
+ s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode));
+ }
+ sendMessage(*s_compiledRegistrar, true);
+ BOOST_REQUIRE(!m_output.empty());
+ }
+ u256 const m_fee = u256("69000000000000000000");
+};
+
+}
+
+/// This is a test suite that tests optimised code!
+BOOST_FIXTURE_TEST_SUITE(SolidityFixedFeeRegistrar, RegistrarTestFramework)
+
+BOOST_AUTO_TEST_CASE(creation)
+{
+ deployRegistrar();
+}
+
+BOOST_AUTO_TEST_CASE(reserve)
+{
+ // Test that reserving works and fee is taken into account.
+ deployRegistrar();
+ string name[] = {"abc", "def", "ghi"};
+ m_sender = Address(0x123);
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name[0])) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[0])) == encodeArgs(h256(0x123)));
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee + 1, encodeDyn(name[1])) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[1])) == encodeArgs(h256(0x123)));
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee - 1, encodeDyn(name[2])) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[2])) == encodeArgs(h256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(double_reserve)
+{
+ // Test that it is not possible to re-reserve from a different address.
+ deployRegistrar();
+ string name = "abc";
+ m_sender = Address(0x123);
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(0x123)));
+
+ m_sender = Address(0x124);
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(0x123)));
+}
+
+BOOST_AUTO_TEST_CASE(properties)
+{
+ // Test setting and retrieving the various properties works.
+ deployRegistrar();
+ string names[] = {"abc", "def", "ghi"};
+ size_t addr = 0x9872543;
+ for (string const& name: names)
+ {
+ addr++;
+ size_t sender = addr + 10007;
+ m_sender = Address(sender);
+ // setting by sender works
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(u256(sender)));
+ BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), u256(addr), u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("addr(string)", encodeDyn(name)) == encodeArgs(addr));
+ BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), addr + 20, u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("subRegistrar(string)", encodeDyn(name)) == encodeArgs(addr + 20));
+ BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), addr + 90, u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(addr + 90));
+ // but not by someone else
+ m_sender = Address(h256(addr + 10007 - 1));
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(sender));
+ BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), addr + 1, u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("addr(string)", encodeDyn(name)) == encodeArgs(addr));
+ BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), addr + 20 + 1, u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("subRegistrar(string)", encodeDyn(name)) == encodeArgs(addr + 20));
+ BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), addr + 90 + 1, u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(addr + 90));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(transfer)
+{
+ deployRegistrar();
+ string name = "abc";
+ m_sender = Address(0x123);
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), u256(123), u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("transfer(string,address)", u256(0x40), u256(555), u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(u256(555)));
+ BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(u256(123)));
+}
+
+BOOST_AUTO_TEST_CASE(disown)
+{
+ deployRegistrar();
+ string name = "abc";
+ m_sender = Address(0x123);
+ BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), u256(123), u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), u256(124), u256(name.length()), name) == encodeArgs());
+ BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), u256(125), u256(name.length()), name) == encodeArgs());
+
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x124)), 0);
+ BOOST_CHECK(callContractFunction("disown(string,address)", u256(0x40), u256(0x124), name.size(), name) == encodeArgs());
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x124)), m_fee);
+
+ BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("addr(string)", encodeDyn(name)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("subRegistrar(string)", encodeDyn(name)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp
new file mode 100644
index 00000000..7c4a9a84
--- /dev/null
+++ b/test/contracts/Wallet.cpp
@@ -0,0 +1,677 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Tests for a (comparatively) complex multisig wallet contract.
+ */
+
+#include <string>
+#include <tuple>
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4535) // calling _set_se_translator requires /EHa
+#endif
+#include <boost/test/unit_test.hpp>
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#include <libdevcore/Hash.h>
+#include <test/libsolidity/solidityExecutionFramework.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+static char const* walletCode = R"DELIMITER(
+//sol Wallet
+// Multi-sig, daily-limited account proxy/wallet.
+// @authors:
+// Gav Wood <g@ethdev.com>
+// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
+// single, or, crucially, each of a number of, designated owners.
+// usage:
+// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
+// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
+// interior is executed.
+contract multiowned {
+
+ // TYPES
+
+ // struct for the status of a pending operation.
+ struct PendingState {
+ uint yetNeeded;
+ uint ownersDone;
+ uint index;
+ }
+
+ // EVENTS
+
+ // this contract only has five types of events: it can accept a confirmation, in which case
+ // we record owner and operation (hash) alongside it.
+ event Confirmation(address owner, bytes32 operation);
+ event Revoke(address owner, bytes32 operation);
+ // some others are in the case of an owner changing.
+ event OwnerChanged(address oldOwner, address newOwner);
+ event OwnerAdded(address newOwner);
+ event OwnerRemoved(address oldOwner);
+ // the last one is emitted if the required signatures change
+ event RequirementChanged(uint newRequirement);
+
+ // MODIFIERS
+
+ // simple single-sig function modifier.
+ modifier onlyowner {
+ if (isOwner(msg.sender))
+ _
+ }
+ // multi-sig function modifier: the operation must have an intrinsic hash in order
+ // that later attempts can be realised as the same underlying operation and
+ // thus count as confirmations.
+ modifier onlymanyowners(bytes32 _operation) {
+ if (confirmAndCheck(_operation))
+ _
+ }
+
+ // METHODS
+
+ // constructor is given number of sigs required to do protected "onlymanyowners" transactions
+ // as well as the selection of addresses capable of confirming them.
+ function multiowned(address[] _owners, uint _required) {
+ m_numOwners = _owners.length + 1;
+ m_owners[1] = uint(msg.sender);
+ m_ownerIndex[uint(msg.sender)] = 1;
+ for (uint i = 0; i < _owners.length; ++i)
+ {
+ m_owners[2 + i] = uint(_owners[i]);
+ m_ownerIndex[uint(_owners[i])] = 2 + i;
+ }
+ m_required = _required;
+ }
+
+ // Revokes a prior confirmation of the given operation
+ function revoke(bytes32 _operation) external {
+ uint ownerIndex = m_ownerIndex[uint(msg.sender)];
+ // make sure they're an owner
+ if (ownerIndex == 0) return;
+ uint ownerIndexBit = 2**ownerIndex;
+ var pending = m_pending[_operation];
+ if (pending.ownersDone & ownerIndexBit > 0) {
+ pending.yetNeeded++;
+ pending.ownersDone -= ownerIndexBit;
+ Revoke(msg.sender, _operation);
+ }
+ }
+
+ // Replaces an owner `_from` with another `_to`.
+ function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
+ if (isOwner(_to)) return;
+ uint ownerIndex = m_ownerIndex[uint(_from)];
+ if (ownerIndex == 0) return;
+
+ clearPending();
+ m_owners[ownerIndex] = uint(_to);
+ m_ownerIndex[uint(_from)] = 0;
+ m_ownerIndex[uint(_to)] = ownerIndex;
+ OwnerChanged(_from, _to);
+ }
+
+ function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
+ if (isOwner(_owner)) return;
+
+ clearPending();
+ if (m_numOwners >= c_maxOwners)
+ reorganizeOwners();
+ if (m_numOwners >= c_maxOwners)
+ return;
+ m_numOwners++;
+ m_owners[m_numOwners] = uint(_owner);
+ m_ownerIndex[uint(_owner)] = m_numOwners;
+ OwnerAdded(_owner);
+ }
+
+ function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
+ uint ownerIndex = m_ownerIndex[uint(_owner)];
+ if (ownerIndex == 0) return;
+ if (m_required > m_numOwners - 1) return;
+
+ m_owners[ownerIndex] = 0;
+ m_ownerIndex[uint(_owner)] = 0;
+ clearPending();
+ reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
+ OwnerRemoved(_owner);
+ }
+
+ function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
+ if (_newRequired > m_numOwners) return;
+ m_required = _newRequired;
+ clearPending();
+ RequirementChanged(_newRequired);
+ }
+
+ function isOwner(address _addr) returns (bool) {
+ return m_ownerIndex[uint(_addr)] > 0;
+ }
+
+ function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
+ var pending = m_pending[_operation];
+ uint ownerIndex = m_ownerIndex[uint(_owner)];
+
+ // make sure they're an owner
+ if (ownerIndex == 0) return false;
+
+ // determine the bit to set for this owner.
+ uint ownerIndexBit = 2**ownerIndex;
+ if (pending.ownersDone & ownerIndexBit == 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ // INTERNAL METHODS
+
+ function confirmAndCheck(bytes32 _operation) internal returns (bool) {
+ // determine what index the present sender is:
+ uint ownerIndex = m_ownerIndex[uint(msg.sender)];
+ // make sure they're an owner
+ if (ownerIndex == 0) return;
+
+ var pending = m_pending[_operation];
+ // if we're not yet working on this operation, switch over and reset the confirmation status.
+ if (pending.yetNeeded == 0) {
+ // reset count of confirmations needed.
+ pending.yetNeeded = m_required;
+ // reset which owners have confirmed (none) - set our bitmap to 0.
+ pending.ownersDone = 0;
+ pending.index = m_pendingIndex.length++;
+ m_pendingIndex[pending.index] = _operation;
+ }
+ // determine the bit to set for this owner.
+ uint ownerIndexBit = 2**ownerIndex;
+ // make sure we (the message sender) haven't confirmed this operation previously.
+ if (pending.ownersDone & ownerIndexBit == 0) {
+ Confirmation(msg.sender, _operation);
+ // ok - check if count is enough to go ahead.
+ if (pending.yetNeeded <= 1) {
+ // enough confirmations: reset and run interior.
+ delete m_pendingIndex[m_pending[_operation].index];
+ delete m_pending[_operation];
+ return true;
+ }
+ else
+ {
+ // not enough: record that this owner in particular confirmed.
+ pending.yetNeeded--;
+ pending.ownersDone |= ownerIndexBit;
+ }
+ }
+ }
+
+ function reorganizeOwners() private returns (bool) {
+ uint free = 1;
+ while (free < m_numOwners)
+ {
+ while (free < m_numOwners && m_owners[free] != 0) free++;
+ while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
+ if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
+ {
+ m_owners[free] = m_owners[m_numOwners];
+ m_ownerIndex[m_owners[free]] = free;
+ m_owners[m_numOwners] = 0;
+ }
+ }
+ }
+
+ function clearPending() internal {
+ uint length = m_pendingIndex.length;
+ for (uint i = 0; i < length; ++i)
+ if (m_pendingIndex[i] != 0)
+ delete m_pending[m_pendingIndex[i]];
+ delete m_pendingIndex;
+ }
+
+ // FIELDS
+
+ // the number of owners that must confirm the same operation before it is run.
+ uint public m_required;
+ // pointer used to find a free slot in m_owners
+ uint public m_numOwners;
+
+ // list of owners
+ uint[256] m_owners;
+ uint constant c_maxOwners = 250;
+ // index on the list of owners to allow reverse lookup
+ mapping(uint => uint) m_ownerIndex;
+ // the ongoing operations.
+ mapping(bytes32 => PendingState) m_pending;
+ bytes32[] m_pendingIndex;
+}
+
+// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
+// on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
+// uses is specified in the modifier.
+contract daylimit is multiowned {
+
+ // MODIFIERS
+
+ // simple modifier for daily limit.
+ modifier limitedDaily(uint _value) {
+ if (underLimit(_value))
+ _
+ }
+
+ // METHODS
+
+ // constructor - stores initial daily limit and records the present day's index.
+ function daylimit(uint _limit) {
+ m_dailyLimit = _limit;
+ m_lastDay = today();
+ }
+ // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
+ function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
+ m_dailyLimit = _newLimit;
+ }
+ // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
+ function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
+ m_spentToday = 0;
+ }
+
+ // INTERNAL METHODS
+
+ // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
+ // returns true. otherwise just returns false.
+ function underLimit(uint _value) internal onlyowner returns (bool) {
+ // reset the spend limit if we're on a different day to last time.
+ if (today() > m_lastDay) {
+ m_spentToday = 0;
+ m_lastDay = today();
+ }
+ // check to see if there's enough left - if so, subtract and return true.
+ if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
+ m_spentToday += _value;
+ return true;
+ }
+ return false;
+ }
+ // determines today's index.
+ function today() private constant returns (uint) { return now / 1 days; }
+
+ // FIELDS
+
+ uint public m_dailyLimit;
+ uint m_spentToday;
+ uint m_lastDay;
+}
+
+// interface contract for multisig proxy contracts; see below for docs.
+contract multisig {
+
+ // EVENTS
+
+ // logged events:
+ // Funds has arrived into the wallet (record how much).
+ event Deposit(address from, uint value);
+ // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
+ event SingleTransact(address owner, uint value, address to, bytes data);
+ // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
+ event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
+ // Confirmation still needed for a transaction.
+ event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
+
+ // FUNCTIONS
+
+ // TODO: document
+ function changeOwner(address _from, address _to) external;
+ function execute(address _to, uint _value, bytes _data) external returns (bytes32);
+ function confirm(bytes32 _h) returns (bool);
+}
+
+// usage:
+// bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
+// Wallet(w).from(anotherOwner).confirm(h);
+contract Wallet is multisig, multiowned, daylimit {
+
+ // TYPES
+
+ // Transaction structure to remember details of transaction lest it need be saved for a later call.
+ struct Transaction {
+ address to;
+ uint value;
+ bytes data;
+ }
+
+ // METHODS
+
+ // constructor - just pass on the owner array to the multiowned and
+ // the limit to daylimit
+ function Wallet(address[] _owners, uint _required, uint _daylimit)
+ multiowned(_owners, _required) daylimit(_daylimit) {
+ }
+
+ // destroys the contract sending everything to `_to`.
+ function kill(address _to) onlymanyowners(sha3(msg.data)) external {
+ selfdestruct(_to);
+ }
+
+ // gets called when no other function matches
+ function() {
+ // just being sent some cash?
+ if (msg.value > 0)
+ Deposit(msg.sender, msg.value);
+ }
+
+ // Outside-visible transact entry point. Executes transacion immediately if below daily spend limit.
+ // If not, goes into multisig process. We provide a hash on return to allow the sender to provide
+ // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
+ // and _data arguments). They still get the option of using them if they want, anyways.
+ function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
+ // first, take the opportunity to check that we're under the daily limit.
+ if (underLimit(_value)) {
+ SingleTransact(msg.sender, _value, _to, _data);
+ // yes - just execute the call.
+ _to.call.value(_value)(_data);
+ return 0;
+ }
+ // determine our operation hash.
+ _r = sha3(msg.data, block.number);
+ if (!confirm(_r) && m_txs[_r].to == 0) {
+ m_txs[_r].to = _to;
+ m_txs[_r].value = _value;
+ m_txs[_r].data = _data;
+ ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
+ }
+ }
+
+ // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
+ // to determine the body of the transaction from the hash provided.
+ function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
+ if (m_txs[_h].to != 0) {
+ m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
+ MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
+ delete m_txs[_h];
+ return true;
+ }
+ }
+
+ // INTERNAL METHODS
+
+ function clearPending() internal {
+ uint length = m_pendingIndex.length;
+ for (uint i = 0; i < length; ++i)
+ delete m_txs[m_pendingIndex[i]];
+ super.clearPending();
+ }
+
+ // FIELDS
+
+ // pending transactions we have at present.
+ mapping (bytes32 => Transaction) m_txs;
+}
+)DELIMITER";
+
+static unique_ptr<bytes> s_compiledWallet;
+
+class WalletTestFramework: public ExecutionFramework
+{
+protected:
+ void deployWallet(
+ u256 const& _value = 0,
+ vector<u256> const& _owners = vector<u256>{},
+ u256 _required = 1,
+ u256 _dailyLimit = 0
+ )
+ {
+ if (!s_compiledWallet)
+ {
+ m_optimize = true;
+ m_compiler.reset(false, m_addStandardSources);
+ m_compiler.addSource("", walletCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
+ s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode));
+ }
+ bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
+ sendMessage(*s_compiledWallet + args, true, _value);
+ BOOST_REQUIRE(!m_output.empty());
+ }
+};
+
+/// This is a test suite that tests optimised code!
+BOOST_FIXTURE_TEST_SUITE(SolidityWallet, WalletTestFramework)
+
+BOOST_AUTO_TEST_CASE(creation)
+{
+ deployWallet(200);
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", ~h256(m_sender, h256::AlignRight)) == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(add_owners)
+{
+ deployWallet(200);
+ Address originalOwner = m_sender;
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
+ // now let the new owner add someone
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
+ // and check that a non-owner cannot add a new owner
+ m_sender = Address(0x50);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false));
+ // finally check that all the owners are there
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(originalOwner, h256::AlignRight)) == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(change_owners)
+{
+ deployWallet(200);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("changeOwner(address,address)", h256(0x12), h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(false));
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(remove_owner)
+{
+ deployWallet(200);
+ // add 10 owners
+ for (unsigned i = 0; i < 10; ++i)
+ {
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
+ }
+ // check they are there again
+ for (unsigned i = 0; i < 10; ++i)
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
+ // remove the odd owners
+ for (unsigned i = 0; i < 10; ++i)
+ if (i % 2 == 1)
+ BOOST_REQUIRE(callContractFunction("removeOwner(address)", h256(0x12 + i)) == encodeArgs());
+ // check the result
+ for (unsigned i = 0; i < 10; ++i)
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(i % 2 == 0));
+ // add them again
+ for (unsigned i = 0; i < 10; ++i)
+ if (i % 2 == 1)
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs());
+ // check everyone is there
+ for (unsigned i = 0; i < 10; ++i)
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(initial_owners)
+{
+ vector<u256> owners{
+ u256("0x00000000000000000000000042c56279432962a17176998a4747d1b4d6ed4367"),
+ u256("0x000000000000000000000000d4d4669f5ba9f4c27d38ef02a358c339b5560c47"),
+ u256("0x000000000000000000000000e6716f9544a56c530d868e4bfbacb172315bdead"),
+ u256("0x000000000000000000000000775e18be7a50a0abb8a4e82b1bd697d79f31fe04"),
+ u256("0x000000000000000000000000f4dd5c3794f1fd0cdc0327a83aa472609c806e99"),
+ u256("0x0000000000000000000000004c9113886af165b2de069d6e99430647e94a9fff"),
+ u256("0x0000000000000000000000003fb1cd2cd96c6d5c0b5eb3322d807b34482481d4")
+ };
+ deployWallet(0, owners, 4, 2);
+ BOOST_CHECK(callContractFunction("m_numOwners()") == encodeArgs(u256(8)));
+ BOOST_CHECK(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true));
+ for (u256 const& owner: owners)
+ {
+ BOOST_CHECK(callContractFunction("isOwner(address)", owner) == encodeArgs(true));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(multisig_value_transfer)
+{
+ deployWallet(200);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
+ // 4 owners, set required to 3
+ BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
+ // check that balance is and stays zero at destination address
+ h256 opHash("6244b4fa93f73e09db0ae52750095ca0364a76b72bc01723c97011fcb876cc9e");
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x13);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x14);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ // now it should go through
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 100);
+}
+
+BOOST_AUTO_TEST_CASE(revoke_addOwner)
+{
+ deployWallet();
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
+ // 4 owners, set required to 3
+ BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
+ // add a new owner
+ Address deployer = m_sender;
+ h256 opHash = sha3(FixedHash<4>(dev::sha3("addOwner(address)")).asBytes() + h256(0x33).asBytes());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false));
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false));
+ // revoke one confirmation
+ m_sender = deployer;
+ BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs());
+ m_sender = Address(0x13);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false));
+ m_sender = Address(0x14);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(revoke_transaction)
+{
+ deployWallet(200);
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
+ // 4 owners, set required to 3
+ BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
+ // create a transaction
+ Address deployer = m_sender;
+ h256 opHash("6244b4fa93f73e09db0ae52750095ca0364a76b72bc01723c97011fcb876cc9e");
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x13);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs());
+ m_sender = deployer;
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x14);
+ BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
+ // now it should go through
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 100);
+}
+
+BOOST_AUTO_TEST_CASE(daylimit)
+{
+ deployWallet(200);
+ BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(0)));
+ BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(100)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(100)));
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs());
+ // 4 owners, set required to 3
+ BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
+
+ // try to send tx over daylimit
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(
+ callContractFunction("execute(address,uint256,bytes)", h256(0x05), 150, 0x60, 0x00) !=
+ encodeArgs(u256(0))
+ );
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ // try to send tx under daylimit by stranger
+ m_sender = Address(0x77);
+ BOOST_REQUIRE(
+ callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) ==
+ encodeArgs(u256(0))
+ );
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
+ // now send below limit by owner
+ m_sender = Address(0x12);
+ BOOST_REQUIRE(
+ callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) ==
+ encodeArgs(u256(0))
+ );
+ BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 90);
+}
+
+BOOST_AUTO_TEST_CASE(daylimit_constructor)
+{
+ deployWallet(200, {}, 1, 20);
+ BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(20)));
+ BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(30)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(30)));
+}
+
+//@todo test data calls
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
new file mode 100644
index 00000000..13665a33
--- /dev/null
+++ b/test/libsolidity/Assembly.cpp
@@ -0,0 +1,130 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris Karapetsas <lefteris@ethdev.com>
+ * @date 2015
+ * Unit tests for Assembly Items from evmasm/Assembly.h
+ */
+
+#include <string>
+#include <iostream>
+#include <boost/test/unit_test.hpp>
+#include <libdevcore/Log.h>
+#include <libevmasm/SourceLocation.h>
+#include <libevmasm/Assembly.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/TypeChecker.h>
+
+using namespace std;
+using namespace dev::eth;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+eth::AssemblyItems compileContract(const string& _sourceCode)
+{
+ ErrorList errors;
+ Parser parser(errors);
+ ASTPointer<SourceUnit> sourceUnit;
+ BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
+ BOOST_CHECK(!!sourceUnit);
+
+ NameAndTypeResolver resolver({}, errors);
+ solAssert(Error::containsOnlyWarnings(errors), "");
+ resolver.registerDeclarations(*sourceUnit);
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
+ if (!Error::containsOnlyWarnings(errors))
+ return AssemblyItems();
+ }
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ TypeChecker checker(errors);
+ BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract));
+ if (!Error::containsOnlyWarnings(errors))
+ return AssemblyItems();
+ }
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ Compiler compiler;
+ compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{});
+
+ return compiler.runtimeAssemblyItems();
+ }
+ BOOST_FAIL("No contract found in source.");
+ return AssemblyItems();
+}
+
+void checkAssemblyLocations(AssemblyItems const& _items, vector<SourceLocation> const& _locations)
+{
+ BOOST_CHECK_EQUAL(_items.size(), _locations.size());
+ for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i)
+ {
+ BOOST_CHECK_MESSAGE(
+ _items[i].location() == _locations[i],
+ "Location mismatch for assembly item " + to_string(i) + ". Found: " +
+ to_string(_items[i].location().start) + "-" +
+ to_string(_items[i].location().end) + ", expected: " +
+ to_string(_locations[i].start) + "-" +
+ to_string(_locations[i].end));
+ }
+}
+
+} // end anonymous namespace
+
+BOOST_AUTO_TEST_SUITE(Assembly)
+
+BOOST_AUTO_TEST_CASE(location_test)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns (uint256 a) {
+ return 16;
+ }
+ }
+ )";
+ shared_ptr<string const> n = make_shared<string>("source");
+ AssemblyItems items = compileContract(sourceCode);
+ vector<SourceLocation> locations =
+ vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
+ vector<SourceLocation>(28, SourceLocation(20, 72, n)) +
+ vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
+ vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
+ vector<SourceLocation>(3, SourceLocation(20, 72, n));
+ checkAssemblyLocations(items, locations);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/CMakeLists.txt b/test/libsolidity/CMakeLists.txt
new file mode 100644
index 00000000..3ceda13b
--- /dev/null
+++ b/test/libsolidity/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_policy(SET CMP0015 NEW)
+
+aux_source_directory(. SRCS)
+
+add_sources(${SRCS})
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
new file mode 100644
index 00000000..9f947af3
--- /dev/null
+++ b/test/libsolidity/GasMeter.cpp
@@ -0,0 +1,237 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Unit tests for the gas estimator.
+ */
+
+#include <test/libsolidity/solidityExecutionFramework.h>
+#include <libevmasm/GasMeter.h>
+#include <libevmasm/KnownState.h>
+#include <libevmasm/PathGasMeter.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/GasEstimator.h>
+#include <libsolidity/interface/SourceReferenceFormatter.h>
+
+using namespace std;
+using namespace dev::eth;
+using namespace dev::solidity;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+class GasMeterTestFramework: public ExecutionFramework
+{
+public:
+ GasMeterTestFramework() { }
+ void compile(string const& _sourceCode)
+ {
+ m_compiler.setSource(_sourceCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed");
+
+ AssemblyItems const* items = m_compiler.runtimeAssemblyItems("");
+ ASTNode const& sourceUnit = m_compiler.ast();
+ BOOST_REQUIRE(items != nullptr);
+ m_gasCosts = GasEstimator::breakToStatementLevel(
+ GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
+ {&sourceUnit}
+ );
+ }
+
+ void testCreationTimeGas(string const& _sourceCode)
+ {
+ EVMSchedule schedule;
+
+ compileAndRun(_sourceCode);
+ auto state = make_shared<KnownState>();
+ PathGasMeter meter(*m_compiler.assemblyItems());
+ GasMeter::GasConsumption gas = meter.estimateMax(0, state);
+ u256 bytecodeSize(m_compiler.runtimeObject().bytecode.size());
+ gas += bytecodeSize * schedule.createDataGas;
+ BOOST_REQUIRE(!gas.isInfinite);
+ BOOST_CHECK(gas.value == m_gasUsed);
+ }
+
+ /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments)
+ /// against the actual gas usage computed by the VM on the given set of argument variants.
+ void testRunTimeGas(string const& _sig, vector<bytes> _argumentVariants)
+ {
+ u256 gasUsed = 0;
+ FixedHash<4> hash(dev::sha3(_sig));
+ for (bytes const& arguments: _argumentVariants)
+ {
+ sendMessage(hash.asBytes() + arguments, false, 0);
+ gasUsed = max(gasUsed, m_gasUsed);
+ }
+
+ GasMeter::GasConsumption gas = GasEstimator::functionalEstimation(
+ *m_compiler.runtimeAssemblyItems(),
+ _sig
+ );
+ BOOST_REQUIRE(!gas.isInfinite);
+ BOOST_CHECK(gas.value == m_gasUsed);
+ }
+
+protected:
+ map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts;
+};
+
+BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework)
+
+BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
+{
+ char const* sourceCode = R"(
+ contract test {
+ bytes x;
+ function f(uint a) returns (uint b) {
+ x.length = a;
+ for (; a < 200; ++a) {
+ x[a] = 9;
+ b = a * a;
+ }
+ return f(a - 1);
+ }
+ }
+ )";
+ compile(sourceCode);
+ for (auto first = m_gasCosts.cbegin(); first != m_gasCosts.cend(); ++first)
+ {
+ auto second = first;
+ for (++second; second != m_gasCosts.cend(); ++second)
+ if (first->first->location().intersects(second->first->location()))
+ {
+ BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!");
+ SourceReferenceFormatter::printSourceLocation(cout, first->first->location(), m_compiler.scanner());
+ SourceReferenceFormatter::printSourceLocation(cout, second->first->location(), m_compiler.scanner());
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(simple_contract)
+{
+ // Tests a simple "deploy contract" code without constructor. The actual contract is not relevant.
+ char const* sourceCode = R"(
+ contract test {
+ bytes32 public shaValue;
+ function f(uint a) {
+ shaValue = sha3(a);
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(store_sha3)
+{
+ char const* sourceCode = R"(
+ contract test {
+ bytes32 public shaValue;
+ function test(uint a) {
+ shaValue = sha3(a);
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(updating_store)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint data;
+ uint data2;
+ function test() {
+ data = 1;
+ data = 2;
+ data2 = 0;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(branches)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint data;
+ uint data2;
+ function f(uint x) {
+ if (x > 7)
+ data2 = 1;
+ else
+ data = 1;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
+}
+
+BOOST_AUTO_TEST_CASE(function_calls)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint data;
+ uint data2;
+ function f(uint x) {
+ if (x > 7)
+ data2 = g(x**8) + 1;
+ else
+ data = 1;
+ }
+ function g(uint x) internal returns (uint) {
+ return data2;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
+}
+
+BOOST_AUTO_TEST_CASE(multiple_external_functions)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint data;
+ uint data2;
+ function f(uint x) {
+ if (x > 7)
+ data2 = g(x**8) + 1;
+ else
+ data = 1;
+ }
+ function g(uint x) returns (uint) {
+ return data2;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
+ testRunTimeGas("g(uint256)", vector<bytes>{encodeArgs(2)});
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp
new file mode 100644
index 00000000..94d3e423
--- /dev/null
+++ b/test/libsolidity/Imports.cpp
@@ -0,0 +1,149 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Tests for high level features like import.
+ */
+
+#include <string>
+#include <boost/test/unit_test.hpp>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/CompilerStack.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(SolidityImports)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(regular_import)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C {}");
+ c.addSource("b", "import \"a\"; contract D is C {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { D d; }");
+ c.addSource("b", "import \"a\"; contract D is C {}");
+ BOOST_CHECK(!c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(import_is_transitive)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { }");
+ c.addSource("b", "import \"a\";");
+ c.addSource("c", "import \"b\"; contract D is C {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(circular_import)
+{
+ CompilerStack c;
+ c.addSource("a", "import \"b\"; contract C { D d; }");
+ c.addSource("b", "import \"a\"; contract D { C c; }");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(relative_import)
+{
+ CompilerStack c;
+ c.addSource("a", "import \"./dir/b\"; contract A is B {}");
+ c.addSource("dir/b", "contract B {}");
+ c.addSource("dir/c", "import \"../a\"; contract C is A {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(relative_import_multiplex)
+{
+ CompilerStack c;
+ c.addSource("a", "contract A {}");
+ c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(simple_alias)
+{
+ CompilerStack c;
+ c.addSource("a", "contract A {}");
+ c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } }");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(library_name_clash)
+{
+ CompilerStack c;
+ c.addSource("a", "library A {}");
+ c.addSource("b", "library A {}");
+ BOOST_CHECK(!c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(library_name_clash_with_contract)
+{
+ CompilerStack c;
+ c.addSource("a", "contract A {}");
+ c.addSource("b", "library A {}");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(complex_import)
+{
+ CompilerStack c;
+ c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } }");
+ c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; "
+ "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} }");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_CASE(name_clash_in_import)
+{
+ CompilerStack c;
+ c.addSource("a", "contract A {}");
+ c.addSource("b", "import \"a\"; contract A {} ");
+ BOOST_CHECK(!c.compile());
+ c.addSource("b", "import \"a\" as A; contract A {} ");
+ BOOST_CHECK(!c.compile());
+ c.addSource("b", "import {A as b} from \"a\"; contract b {} ");
+ BOOST_CHECK(!c.compile());
+ c.addSource("b", "import {A} from \"a\"; contract A {} ");
+ BOOST_CHECK(!c.compile());
+ c.addSource("b", "import {A} from \"a\"; contract B {} ");
+ BOOST_CHECK(c.compile());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
new file mode 100644
index 00000000..77b39978
--- /dev/null
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -0,0 +1,633 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @author Marek Kotewicz <marek@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity compiler JSON Interface output.
+ */
+
+#include "../TestHelper.h"
+#include <libsolidity/interface/CompilerStack.h>
+#include <json/json.h>
+#include <libdevcore/Exceptions.h>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+class JSONInterfaceChecker
+{
+public:
+ JSONInterfaceChecker(): m_compilerStack(false) {}
+
+ void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
+ {
+ ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing contract failed");
+ std::string generatedInterfaceString = m_compilerStack.metadata("", DocumentationType::ABIInterface);
+ Json::Value generatedInterface;
+ m_reader.parse(generatedInterfaceString, generatedInterface);
+ Json::Value expectedInterface;
+ m_reader.parse(_expectedInterfaceString, expectedInterface);
+ BOOST_CHECK_MESSAGE(
+ expectedInterface == generatedInterface,
+ "Expected:\n" << expectedInterface.toStyledString() << "\n but got:\n" << generatedInterface.toStyledString()
+ );
+ }
+
+private:
+ CompilerStack m_compilerStack;
+ Json::Reader m_reader;
+};
+
+BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker)
+
+BOOST_AUTO_TEST_CASE(basic_test)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ "}\n";
+
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(empty_contract)
+{
+ char const* sourceCode = "contract test {\n"
+ "}\n";
+ char const* interface = "[]";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(multiple_methods)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ " function g(uint b) returns(uint e) { return b * 8; }\n"
+ "}\n";
+
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "name": "g",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "e",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(multiple_params)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a, uint b) returns(uint d) { return a + b; }\n"
+ "}\n";
+
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ },
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(multiple_methods_order)
+{
+ // methods are expected to be in alpabetical order
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ " function c(uint b) returns(uint e) { return b * 8; }\n"
+ "}\n";
+
+ char const* interface = R"([
+ {
+ "name": "c",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "e",
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(const_function)
+{
+ char const* sourceCode = "contract test {\n"
+ " function foo(uint a, uint b) returns(uint d) { return a + b; }\n"
+ " function boo(uint32 a) constant returns(uint b) { return a * 4; }\n"
+ "}\n";
+
+ char const* interface = R"([
+ {
+ "name": "foo",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ },
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "name": "boo",
+ "constant": true,
+ "type": "function",
+ "inputs": [{
+ "name": "a",
+ "type": "uint32"
+ }],
+ "outputs": [
+ {
+ "name": "b",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(exclude_fallback_function)
+{
+ char const* sourceCode = "contract test { function() {} }";
+
+ char const* interface = "[]";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(events)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ " event e1(uint b, address indexed c); \n"
+ " event e2(); \n"
+ "}\n";
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "a",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "d",
+ "type": "uint256"
+ }
+ ]
+ },
+ {
+ "name": "e1",
+ "type": "event",
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "name": "b",
+ "type": "uint256"
+ },
+ {
+ "indexed": true,
+ "name": "c",
+ "type": "address"
+ }
+ ]
+ },
+ {
+ "name": "e2",
+ "type": "event",
+ "anonymous": false,
+ "inputs": []
+ }
+
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(events_anonymous)
+{
+ char const* sourceCode = "contract test {\n"
+ " event e() anonymous; \n"
+ "}\n";
+ char const* interface = R"([
+ {
+ "name": "e",
+ "type": "event",
+ "anonymous": true,
+ "inputs": []
+ }
+
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(inherited)
+{
+ char const* sourceCode =
+ " contract Base { \n"
+ " function baseFunction(uint p) returns (uint i) { return p; } \n"
+ " event baseEvent(bytes32 indexed evtArgBase); \n"
+ " } \n"
+ " contract Derived is Base { \n"
+ " function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n"
+ " event derivedEvent(uint indexed evtArgDerived); \n"
+ " }";
+
+ char const* interface = R"([
+ {
+ "name": "baseFunction",
+ "constant": false,
+ "type": "function",
+ "inputs":
+ [{
+ "name": "p",
+ "type": "uint256"
+ }],
+ "outputs":
+ [{
+ "name": "i",
+ "type": "uint256"
+ }]
+ },
+ {
+ "name": "derivedFunction",
+ "constant": false,
+ "type": "function",
+ "inputs":
+ [{
+ "name": "p",
+ "type": "bytes32"
+ }],
+ "outputs":
+ [{
+ "name": "i",
+ "type": "bytes32"
+ }]
+ },
+ {
+ "name": "derivedEvent",
+ "type": "event",
+ "anonymous": false,
+ "inputs":
+ [{
+ "indexed": true,
+ "name": "evtArgDerived",
+ "type": "uint256"
+ }]
+ },
+ {
+ "name": "baseEvent",
+ "type": "event",
+ "anonymous": false,
+ "inputs":
+ [{
+ "indexed": true,
+ "name": "evtArgBase",
+ "type": "bytes32"
+ }]
+ }])";
+
+
+ checkInterface(sourceCode, interface);
+}
+BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint, uint k) returns(uint ret_k, uint ret_g){
+ uint g = 8;
+ ret_k = k;
+ ret_g = g;
+ }
+ })";
+
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "k",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "ret_k",
+ "type": "uint256"
+ },
+ {
+ "name": "ret_g",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint k) returns(uint){
+ return k;
+ }
+ })";
+
+ char const* interface = R"([
+ {
+ "name": "f",
+ "constant": false,
+ "type": "function",
+ "inputs": [
+ {
+ "name": "k",
+ "type": "uint256"
+ }
+ ],
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ]
+ }
+ ])";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(constructor_abi)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test(uint param1, test param2, bool param3) {}
+ }
+ )";
+
+ char const* interface = R"([
+ {
+ "inputs": [
+ {
+ "name": "param1",
+ "type": "uint256"
+ },
+ {
+ "name": "param2",
+ "type": "address"
+ },
+ {
+ "name": "param3",
+ "type": "bool"
+ }
+ ],
+ "type": "constructor"
+ }
+ ])";
+ checkInterface(sourceCode, interface);
+}
+
+
+BOOST_AUTO_TEST_CASE(return_param_in_abi)
+{
+ // bug #1801
+ char const* sourceCode = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test(ActionChoices param) {}
+ function ret() returns(ActionChoices){
+ ActionChoices action = ActionChoices.GoLeft;
+ return action;
+ }
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "inputs" : [],
+ "name" : "ret",
+ "outputs" : [
+ {
+ "name" : "",
+ "type" : "uint8"
+ }
+ ],
+ "type" : "function"
+ },
+ {
+ "inputs": [
+ {
+ "name": "param",
+ "type": "uint8"
+ }
+ ],
+ "type": "constructor"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(strings_and_arrays)
+{
+ // bug #1801
+ char const* sourceCode = R"(
+ contract test {
+ function f(string a, bytes b, uint[] c) external {}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "name": "f",
+ "inputs": [
+ { "name": "a", "type": "string" },
+ { "name": "b", "type": "bytes" },
+ { "name": "c", "type": "uint256[]" }
+ ],
+ "outputs": [],
+ "type" : "function"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_CASE(library_function)
+{
+ char const* sourceCode = R"(
+ library test {
+ struct StructType { uint a; }
+ function f(StructType storage b, uint[] storage c, test d) returns (uint[] e, StructType storage f){}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "name": "f",
+ "inputs": [
+ { "name": "b", "type": "test.StructType storage" },
+ { "name": "c", "type": "uint256[] storage" },
+ { "name": "d", "type": "test" }
+ ],
+ "outputs": [
+ { "name": "e", "type": "uint256[]" },
+ { "name": "f", "type": "test.StructType storage" }
+ ],
+ "type" : "function"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
new file mode 100644
index 00000000..56f07415
--- /dev/null
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -0,0 +1,6493 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity expression compiler, testing the behaviour of the code.
+ */
+
+#include <string>
+#include <tuple>
+#include <boost/test/unit_test.hpp>
+#include <libdevcore/Hash.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <test/libsolidity/solidityExecutionFramework.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, ExecutionFramework)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return a * 7; }, 0, 100);
+}
+
+BOOST_AUTO_TEST_CASE(empty_contract)
+{
+ char const* sourceCode = "contract test {\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()).empty());
+}
+
+BOOST_AUTO_TEST_CASE(exp_operator)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a) returns(uint d) { return 2 ** a; }
+ })";
+ compileAndRun(sourceCode);
+ testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return u256(1 << a.convert_to<int>()); }, 0, 16);
+}
+
+BOOST_AUTO_TEST_CASE(exp_operator_const)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) { return 2 ** 3; }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(8)));
+}
+
+BOOST_AUTO_TEST_CASE(exp_operator_const_signed)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(int d) { return (-2) ** 3; }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(-8)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_true_literal)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) {
+ return true ? 5 : 10;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(5)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_false_literal)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) {
+ return false ? 5 : 10;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(10)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_multiple)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint x) returns(uint d) {
+ return x > 100 ?
+ x > 1000 ? 1000 : 100
+ :
+ x > 50 ? 50 : 10;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(1001)) == toBigEndian(u256(1000)));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(500)) == toBigEndian(u256(100)));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(80)) == toBigEndian(u256(50)));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(40)) == toBigEndian(u256(10)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(bool cond, uint v) returns (uint a, uint b) {
+ cond ? a = v : b = v;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool,uint256)", true, u256(20)) == encodeArgs(u256(20), u256(0)));
+ BOOST_CHECK(callContractFunction("f(bool,uint256)", false, u256(20)) == encodeArgs(u256(0), u256(20)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_1)
+{
+ char const* sourceCode = R"(
+ contract test {
+ bytes2[2] data1;
+ function f(bool cond) returns (uint) {
+ bytes2[2] memory x;
+ x[0] = "aa";
+ bytes2[2] memory y;
+ y[0] = "bb";
+
+ data1 = cond ? x : y;
+
+ uint ret = 0;
+ if (data1[0] == "aa")
+ {
+ ret = 1;
+ }
+
+ if (data1[0] == "bb")
+ {
+ ret = 2;
+ }
+
+ return ret;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_2)
+{
+ char const* sourceCode = R"(
+ contract test {
+ bytes2[2] data1;
+ function f(bool cond) returns (uint) {
+ data1[0] = "cc";
+
+ bytes2[2] memory x;
+ bytes2[2] memory y;
+ y[0] = "bb";
+
+ x = cond ? y : data1;
+
+ uint ret = 0;
+ if (x[0] == "bb")
+ {
+ ret = 1;
+ }
+
+ if (x[0] == "cc")
+ {
+ ret = 2;
+ }
+
+ return ret;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_different_types)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(bool cond) returns (uint) {
+ uint8 x = 0xcd;
+ uint16 y = 0xabab;
+ return cond ? x : y;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(0xcd)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(0xabab)));
+}
+
+/* let's add this back when I figure out the correct type conversion.
+BOOST_AUTO_TEST_CASE(conditional_expression_string_literal)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(bool cond) returns (bytes32) {
+ return cond ? "true" : "false";
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(string("true", 4)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(string("false", 5)));
+}
+*/
+
+BOOST_AUTO_TEST_CASE(conditional_expression_tuples)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(bool cond) returns (uint, uint) {
+ return cond ? (1, 2) : (3, 4);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1), u256(2)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(3), u256(4)));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_functions)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function x() returns (uint) { return 1; }
+ function y() returns (uint) { return 2; }
+
+ function f(bool cond) returns (uint) {
+ var z = cond ? x : y;
+ return z();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(recursive_calls)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint n) returns(uint nfac) {\n"
+ " if (n <= 1) return 1;\n"
+ " else return n * f(n - 1);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ function<u256(u256)> recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256
+ {
+ if (n <= 1)
+ return 1;
+ else
+ return n * recursive_calls_cpp(n - 1);
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", recursive_calls_cpp, 0, 5);
+}
+
+BOOST_AUTO_TEST_CASE(multiple_functions)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() returns(uint n) { return 0; }\n"
+ " function b() returns(uint n) { return 1; }\n"
+ " function c() returns(uint n) { return 2; }\n"
+ " function f() returns(uint n) { return 3; }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("a()", bytes()) == toBigEndian(u256(0)));
+ BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(1)));
+ BOOST_CHECK(callContractFunction("c()", bytes()) == toBigEndian(u256(2)));
+ BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(3)));
+ BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()) == bytes());
+}
+
+BOOST_AUTO_TEST_CASE(named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
+ " function b() returns (uint r) { r = a({a: 1, b: 2, c: 3}); }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123)));
+}
+
+BOOST_AUTO_TEST_CASE(disorder_named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
+ " function b() returns (uint r) { r = a({c: 3, a: 1, b: 2}); }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(123)));
+}
+
+BOOST_AUTO_TEST_CASE(while_loop)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint n) returns(uint nfac) {\n"
+ " nfac = 1;\n"
+ " var i = 2;\n"
+ " while (i <= n) nfac *= i++;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto while_loop_cpp = [](u256 const& n) -> u256
+ {
+ u256 nfac = 1;
+ u256 i = 2;
+ while (i <= n)
+ nfac *= i++;
+
+ return nfac;
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
+}
+
+BOOST_AUTO_TEST_CASE(nested_loops)
+{
+ // tests that break and continue statements in nested loops jump to the correct place
+ char const* sourceCode = "contract test {\n"
+ " function f(uint x) returns(uint y) {\n"
+ " while (x > 1) {\n"
+ " if (x == 10) break;\n"
+ " while (x > 5) {\n"
+ " if (x == 8) break;\n"
+ " x--;\n"
+ " if (x == 6) continue;\n"
+ " return x;\n"
+ " }\n"
+ " x--;\n"
+ " if (x == 3) continue;\n"
+ " break;\n"
+ " }\n"
+ " return x;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto nested_loops_cpp = [](u256 n) -> u256
+ {
+ while (n > 1)
+ {
+ if (n == 10)
+ break;
+ while (n > 5)
+ {
+ if (n == 8)
+ break;
+ n--;
+ if (n == 6)
+ continue;
+ return n;
+ }
+ n--;
+ if (n == 3)
+ continue;
+ break;
+ }
+
+ return n;
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", nested_loops_cpp, 0, 12);
+}
+
+BOOST_AUTO_TEST_CASE(for_loop)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint n) returns(uint nfac) {\n"
+ " nfac = 1;\n"
+ " for (var i = 2; i <= n; i++)\n"
+ " nfac *= i;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto for_loop_cpp = [](u256 const& n) -> u256
+ {
+ u256 nfac = 1;
+ for (auto i = 2; i <= n; i++)
+ nfac *= i;
+ return nfac;
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", for_loop_cpp, 0, 5);
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_empty)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() returns(uint ret) {\n"
+ " ret = 1;\n"
+ " for (;;)\n"
+ " {\n"
+ " ret += 1;\n"
+ " if (ret >= 10) break;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto for_loop_empty_cpp = []() -> u256
+ {
+ u256 ret = 1;
+ for (;;)
+ {
+ ret += 1;
+ if (ret >= 10) break;
+ }
+ return ret;
+ };
+
+ testSolidityAgainstCpp("f()", for_loop_empty_cpp);
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint n) returns(uint nfac) {\n"
+ " nfac = 1;\n"
+ " uint256 i;\n"
+ " for (i = 2; i <= n; i++)\n"
+ " nfac *= i;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256
+ {
+ u256 nfac = 1;
+ u256 i;
+ for (i = 2; i <= n; i++)
+ nfac *= i;
+ return nfac;
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", for_loop_simple_init_expr_cpp, 0, 5);
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_break_continue)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint n) returns (uint r)
+ {
+ uint i = 1;
+ uint k = 0;
+ for (i *= 5; k < n; i *= 7)
+ {
+ k++;
+ i += 4;
+ if (n % 3 == 0)
+ break;
+ i += 9;
+ if (n % 2 == 0)
+ continue;
+ i += 19;
+ }
+ return i;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+
+ auto breakContinue = [](u256 const& n) -> u256
+ {
+ u256 i = 1;
+ u256 k = 0;
+ for (i *= 5; k < n; i *= 7)
+ {
+ k++;
+ i += 4;
+ if (n % 3 == 0)
+ break;
+ i += 9;
+ if (n % 2 == 0)
+ continue;
+ i += 19;
+ }
+ return i;
+ };
+
+ testSolidityAgainstCppOnRange("f(uint256)", breakContinue, 0, 10);
+}
+
+BOOST_AUTO_TEST_CASE(calling_other_functions)
+{
+ char const* sourceCode = "contract collatz {\n"
+ " function run(uint x) returns(uint y) {\n"
+ " while ((y = x) > 1) {\n"
+ " if (x % 2 == 0) x = evenStep(x);\n"
+ " else x = oddStep(x);\n"
+ " }\n"
+ " }\n"
+ " function evenStep(uint x) returns(uint y) {\n"
+ " return x / 2;\n"
+ " }\n"
+ " function oddStep(uint x) returns(uint y) {\n"
+ " return 3 * x + 1;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto evenStep_cpp = [](u256 const& n) -> u256
+ {
+ return n / 2;
+ };
+
+ auto oddStep_cpp = [](u256 const& n) -> u256
+ {
+ return 3 * n + 1;
+ };
+
+ auto collatz_cpp = [&evenStep_cpp, &oddStep_cpp](u256 n) -> u256
+ {
+ u256 y;
+ while ((y = n) > 1)
+ {
+ if (n % 2 == 0)
+ n = evenStep_cpp(n);
+ else
+ n = oddStep_cpp(n);
+ }
+ return y;
+ };
+
+ testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(0));
+ testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(1));
+ testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(2));
+ testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(8));
+ testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(127));
+}
+
+BOOST_AUTO_TEST_CASE(many_local_variables)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run(uint x1, uint x2, uint x3) returns(uint y) {\n"
+ " var a = 0x1; var b = 0x10; var c = 0x100;\n"
+ " y = a + b + c + x1 + x2 + x3;\n"
+ " y += b + x2;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256
+ {
+ u256 a = 0x1;
+ u256 b = 0x10;
+ u256 c = 0x100;
+ u256 y = a + b + c + x1 + x2 + x3;
+ return y + b + x2;
+ };
+ testSolidityAgainstCpp("run(uint256,uint256,uint256)", f, u256(0x1000), u256(0x10000), u256(0x100000));
+}
+
+BOOST_AUTO_TEST_CASE(packing_unpacking_types)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run(bool a, uint32 b, uint64 c) returns(uint256 y) {\n"
+ " if (a) y = 1;\n"
+ " y = y * 0x100000000 | ~b;\n"
+ " y = y * 0x10000000000000000 | ~c;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("run(bool,uint32,uint64)", true, fromHex("0f0f0f0f"), fromHex("f0f0f0f0f0f0f0f0"))
+ == fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f"));
+}
+
+BOOST_AUTO_TEST_CASE(packing_signed_types)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run() returns(int8 y) {\n"
+ " uint8 x = 0xfa;\n"
+ " return int8(x);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("run()")
+ == fromHex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa"));
+}
+
+BOOST_AUTO_TEST_CASE(multiple_return_values)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run(bool x1, uint x2) returns(uint y1, bool y2, uint y3) {\n"
+ " y1 = x2; y2 = x1;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("run(bool,uint256)", true, 0xcd) == encodeArgs(0xcd, true, 0));
+}
+
+BOOST_AUTO_TEST_CASE(short_circuiting)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run(uint x) returns(uint y) {\n"
+ " x == 0 || ((x = 8) > 0);\n"
+ " return x;"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ auto short_circuiting_cpp = [](u256 n) -> u256
+ {
+ (void)(n == 0 || (n = 8) > 0);
+ return n;
+ };
+
+ testSolidityAgainstCppOnRange("run(uint256)", short_circuiting_cpp, 0, 2);
+}
+
+BOOST_AUTO_TEST_CASE(high_bits_cleaning)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run() returns(uint256 y) {\n"
+ " uint32 t = uint32(0xffffffff);\n"
+ " uint32 x = t + 10;\n"
+ " if (x >= 0xffffffff) return 0;\n"
+ " return x;"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto high_bits_cleaning_cpp = []() -> u256
+ {
+ uint32_t t = uint32_t(0xffffffff);
+ uint32_t x = t + 10;
+ if (x >= 0xffffffff)
+ return 0;
+ return x;
+ };
+ testSolidityAgainstCpp("run()", high_bits_cleaning_cpp);
+}
+
+BOOST_AUTO_TEST_CASE(sign_extension)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run() returns(uint256 y) {\n"
+ " int64 x = -int32(0xff);\n"
+ " if (x >= 0xff) return 0;\n"
+ " return -uint256(x);"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto sign_extension_cpp = []() -> u256
+ {
+ int64_t x = -int32_t(0xff);
+ if (x >= 0xff)
+ return 0;
+ return u256(x) * -1;
+ };
+ testSolidityAgainstCpp("run()", sign_extension_cpp);
+}
+
+BOOST_AUTO_TEST_CASE(small_unsigned_types)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run() returns(uint256 y) {\n"
+ " uint32 t = uint32(0xffffff);\n"
+ " uint32 x = t * 0xffffff;\n"
+ " return x / 0x100;"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto small_unsigned_types_cpp = []() -> u256
+ {
+ uint32_t t = uint32_t(0xffffff);
+ uint32_t x = t * 0xffffff;
+ return x / 0x100;
+ };
+ testSolidityAgainstCpp("run()", small_unsigned_types_cpp);
+}
+
+BOOST_AUTO_TEST_CASE(small_signed_types)
+{
+ char const* sourceCode = "contract test {\n"
+ " function run() returns(int256 y) {\n"
+ " return -int32(10) * -int64(20);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto small_signed_types_cpp = []() -> u256
+ {
+ return -int32_t(10) * -int64_t(20);
+ };
+ testSolidityAgainstCpp("run()", small_signed_types_cpp);
+}
+
+BOOST_AUTO_TEST_CASE(strings)
+{
+ char const* sourceCode = "contract test {\n"
+ " function fixedBytes() returns(bytes32 ret) {\n"
+ " return \"abc\\x00\\xff__\";\n"
+ " }\n"
+ " function pipeThrough(bytes2 small, bool one) returns(bytes16 large, bool oneRet) {\n"
+ " oneRet = one;\n"
+ " large = small;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("fixedBytes()") == encodeArgs(string("abc\0\xff__", 7)));
+ BOOST_CHECK(callContractFunction("pipeThrough(bytes2,bool)", string("\0\x02", 2), true) == encodeArgs(string("\0\x2", 2), true));
+}
+
+BOOST_AUTO_TEST_CASE(inc_dec_operators)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint8 x;
+ uint v;
+ function f() returns (uint r) {
+ uint a = 6;
+ r = a;
+ r += (a++) * 0x10;
+ r += (++a) * 0x100;
+ v = 3;
+ r += (v++) * 0x1000;
+ r += (++v) * 0x10000;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(0x53866));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_comparison)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns (bool) {
+ bytes2 a = "a";
+ bytes2 x = "aa";
+ bytes2 b = "b";
+ return a < x && x < b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(state_smoke_test)
+{
+ char const* sourceCode = "contract test {\n"
+ " uint256 value1;\n"
+ " uint256 value2;\n"
+ " function get(uint8 which) returns (uint256 value) {\n"
+ " if (which == 0) return value1;\n"
+ " else return value2;\n"
+ " }\n"
+ " function set(uint8 which, uint256 value) {\n"
+ " if (which == 0) value1 = value;\n"
+ " else value2 = value;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x1234) == encodeArgs());
+ BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x01), 0x8765) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get(uint8)", byte( 0x00)) == encodeArgs(0x1234));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(0x8765));
+ BOOST_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x3) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(0x3));
+}
+
+BOOST_AUTO_TEST_CASE(compound_assign)
+{
+ char const* sourceCode = "contract test {\n"
+ " uint value1;\n"
+ " uint value2;\n"
+ " function f(uint x, uint y) returns (uint w) {\n"
+ " uint value3 = y;"
+ " value1 += x;\n"
+ " value3 *= x;"
+ " value2 *= value3 + value1;\n"
+ " return value2 += 7;"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ u256 value1;
+ u256 value2;
+ auto f = [&](u256 const& _x, u256 const& _y) -> u256
+ {
+ u256 value3 = _y;
+ value1 += _x;
+ value3 *= _x;
+ value2 *= value3 + value1;
+ return value2 += 7;
+ };
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(0), u256(6));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(1), u256(3));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(2), u256(25));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(3), u256(69));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(4), u256(84));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(5), u256(2));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(6), u256(51));
+ testSolidityAgainstCpp("f(uint256,uint256)", f, u256(7), u256(48));
+}
+
+BOOST_AUTO_TEST_CASE(simple_mapping)
+{
+ char const* sourceCode = "contract test {\n"
+ " mapping(uint8 => uint8) table;\n"
+ " function get(uint8 k) returns (uint8 v) {\n"
+ " return table[k];\n"
+ " }\n"
+ " function set(uint8 k, uint8 v) {\n"
+ " table[k] = v;\n"
+ " }\n"
+ "}";
+ compileAndRun(sourceCode);
+
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0)) == encodeArgs(byte(0x00)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0x00)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00)));
+ callContractFunction("set(uint8,uint8)", byte(0x01), byte(0xa1));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0x00)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0xa1)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00)));
+ callContractFunction("set(uint8,uint8)", byte(0x00), byte(0xef));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0xef)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0xa1)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00)));
+ callContractFunction("set(uint8,uint8)", byte(0x01), byte(0x05));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x00)) == encodeArgs(byte(0xef)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0x05)));
+ BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00)));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_state)
+{
+ char const* sourceCode = "contract Ballot {\n"
+ " mapping(address => bool) canVote;\n"
+ " mapping(address => uint) voteCount;\n"
+ " mapping(address => bool) voted;\n"
+ " function getVoteCount(address addr) returns (uint retVoteCount) {\n"
+ " return voteCount[addr];\n"
+ " }\n"
+ " function grantVoteRight(address addr) {\n"
+ " canVote[addr] = true;\n"
+ " }\n"
+ " function vote(address voter, address vote) returns (bool success) {\n"
+ " if (!canVote[voter] || voted[voter]) return false;\n"
+ " voted[voter] = true;\n"
+ " voteCount[vote] = voteCount[vote] + 1;\n"
+ " return true;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ class Ballot
+ {
+ public:
+ u256 getVoteCount(u160 _address) { return m_voteCount[_address]; }
+ void grantVoteRight(u160 _address) { m_canVote[_address] = true; }
+ bool vote(u160 _voter, u160 _vote)
+ {
+ if (!m_canVote[_voter] || m_voted[_voter]) return false;
+ m_voted[_voter] = true;
+ m_voteCount[_vote]++;
+ return true;
+ }
+ private:
+ map<u160, bool> m_canVote;
+ map<u160, u256> m_voteCount;
+ map<u160, bool> m_voted;
+ } ballot;
+
+ auto getVoteCount = bind(&Ballot::getVoteCount, &ballot, _1);
+ auto grantVoteRight = bind(&Ballot::grantVoteRight, &ballot, _1);
+ auto vote = bind(&Ballot::vote, &ballot, _1, _2);
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+ // voting without vote right should be rejected
+ testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+ // grant vote rights
+ testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(0));
+ testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(1));
+ // vote, should increase 2's vote count
+ testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+ // vote again, should be rejected
+ testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+ // vote without right to vote
+ testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+ // grant vote right and now vote again
+ testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(2));
+ testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
+ testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
+{
+ char const* sourceCode = "contract test {\n"
+ " uint value;\n"
+ " mapping(uint => uint) table;\n"
+ " function f(uint x) returns (uint y) {\n"
+ " value = x;\n"
+ " if (x > 0) table[++value] = 8;\n"
+ " if (x > 1) value--;\n"
+ " if (x > 2) table[value]++;\n"
+ " return --table[value++];\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ u256 value = 0;
+ map<u256, u256> table;
+ auto f = [&](u256 const& _x) -> u256
+ {
+ value = _x;
+ if (_x > 0)
+ table[++value] = 8;
+ if (_x > 1)
+ value --;
+ if (_x > 2)
+ table[value]++;
+ return --table[value++];
+ };
+ testSolidityAgainstCppOnRange("f(uint256)", f, 0, 5);
+}
+
+BOOST_AUTO_TEST_CASE(multi_level_mapping)
+{
+ char const* sourceCode = "contract test {\n"
+ " mapping(uint => mapping(uint => uint)) table;\n"
+ " function f(uint x, uint y, uint z) returns (uint w) {\n"
+ " if (z == 0) return table[x][y];\n"
+ " else return table[x][y] = z;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+
+ map<u256, map<u256, u256>> table;
+ auto f = [&](u256 const& _x, u256 const& _y, u256 const& _z) -> u256
+ {
+ if (_z == 0) return table[_x][_y];
+ else return table[_x][_y] = _z;
+ };
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(9));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(7));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
+ testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
+}
+
+BOOST_AUTO_TEST_CASE(structs)
+{
+ char const* sourceCode = "contract test {\n"
+ " struct s1 {\n"
+ " uint8 x;\n"
+ " bool y;\n"
+ " }\n"
+ " struct s2 {\n"
+ " uint32 z;\n"
+ " s1 s1data;\n"
+ " mapping(uint8 => s2) recursive;\n"
+ " }\n"
+ " s2 data;\n"
+ " function check() returns (bool ok) {\n"
+ " return data.z == 1 && data.s1data.x == 2 && \n"
+ " data.s1data.y == true && \n"
+ " data.recursive[3].recursive[4].z == 5 && \n"
+ " data.recursive[4].recursive[3].z == 6 && \n"
+ " data.recursive[0].s1data.y == false && \n"
+ " data.recursive[4].z == 9;\n"
+ " }\n"
+ " function set() {\n"
+ " data.z = 1;\n"
+ " data.s1data.x = 2;\n"
+ " data.s1data.y = true;\n"
+ " data.recursive[3].recursive[4].z = 5;\n"
+ " data.recursive[4].recursive[3].z = 6;\n"
+ " data.recursive[0].s1data.y = false;\n"
+ " data.recursive[4].z = 9;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("check()") == encodeArgs(false));
+ BOOST_CHECK(callContractFunction("set()") == bytes());
+ BOOST_CHECK(callContractFunction("check()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(struct_reference)
+{
+ char const* sourceCode = "contract test {\n"
+ " struct s2 {\n"
+ " uint32 z;\n"
+ " mapping(uint8 => s2) recursive;\n"
+ " }\n"
+ " s2 data;\n"
+ " function check() returns (bool ok) {\n"
+ " return data.z == 2 && \n"
+ " data.recursive[0].z == 3 && \n"
+ " data.recursive[0].recursive[1].z == 0 && \n"
+ " data.recursive[0].recursive[0].z == 1;\n"
+ " }\n"
+ " function set() {\n"
+ " data.z = 2;\n"
+ " var map = data.recursive;\n"
+ " s2 inner = map[0];\n"
+ " inner.z = 3;\n"
+ " inner.recursive[0].z = inner.recursive[1].z + 1;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("check()") == encodeArgs(false));
+ BOOST_CHECK(callContractFunction("set()") == bytes());
+ BOOST_CHECK(callContractFunction("check()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(deleteStruct)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct topStruct {
+ nestedStruct nstr;
+ emptyStruct empty;
+ uint topValue;
+ mapping (uint => uint) topMapping;
+ }
+ uint toDelete;
+ topStruct str;
+ struct nestedStruct {
+ uint nestedValue;
+ mapping (uint => bool) nestedMapping;
+ }
+ struct emptyStruct{
+ }
+ function test(){
+ toDelete = 5;
+ str.topValue = 1;
+ str.topMapping[0] = 1;
+ str.topMapping[1] = 2;
+
+ str.nstr.nestedValue = 2;
+ str.nstr.nestedMapping[0] = true;
+ str.nstr.nestedMapping[1] = false;
+ delete str;
+ delete toDelete;
+ }
+ function getToDelete() returns (uint res){
+ res = toDelete;
+ }
+ function getTopValue() returns(uint topValue){
+ topValue = str.topValue;
+ }
+ function getNestedValue() returns(uint nestedValue){
+ nestedValue = str.nstr.nestedValue;
+ }
+ function getTopMapping(uint index) returns(uint ret) {
+ ret = str.topMapping[index];
+ }
+ function getNestedMapping(uint index) returns(bool ret) {
+ return str.nstr.nestedMapping[index];
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getToDelete()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("getTopValue()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("getNestedValue()") == encodeArgs(0));
+ // mapping values should be the same
+ BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 0) == encodeArgs(1));
+ BOOST_CHECK(callContractFunction("getTopMapping(uint256)", 1) == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 0) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("getNestedMapping(uint256)", 1) == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(deleteLocal)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function delLocal() returns (uint res){
+ uint v = 5;
+ delete v;
+ res = v;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(0));
+}
+
+BOOST_AUTO_TEST_CASE(deleteLocals)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function delLocal() returns (uint res1, uint res2){
+ uint v = 5;
+ uint w = 6;
+ uint x = 7;
+ delete v;
+ res1 = w;
+ res2 = x;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("delLocal()") == encodeArgs(6, 7));
+}
+
+BOOST_AUTO_TEST_CASE(constructor)
+{
+ char const* sourceCode = "contract test {\n"
+ " mapping(uint => uint) data;\n"
+ " function test() {\n"
+ " data[7] = 8;\n"
+ " }\n"
+ " function get(uint key) returns (uint value) {\n"
+ " return data[key];"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ map<u256, byte> data;
+ data[7] = 8;
+ auto get = [&](u256 const& _x) -> u256
+ {
+ return data[_x];
+ };
+ testSolidityAgainstCpp("get(uint256)", get, u256(6));
+ testSolidityAgainstCpp("get(uint256)", get, u256(7));
+}
+
+BOOST_AUTO_TEST_CASE(simple_accessor)
+{
+ char const* sourceCode = "contract test {\n"
+ " uint256 public data;\n"
+ " function test() {\n"
+ " data = 8;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("data()") == encodeArgs(8));
+}
+
+BOOST_AUTO_TEST_CASE(array_accessor)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint[8] public data;
+ uint[] public dynamicData;
+ uint24[] public smallTypeData;
+ struct st { uint a; uint[] finalArray; }
+ mapping(uint256 => mapping(uint256 => st[5])) public multiple_map;
+
+ function test() {
+ data[0] = 8;
+ dynamicData.length = 3;
+ dynamicData[2] = 8;
+ smallTypeData.length = 128;
+ smallTypeData[1] = 22;
+ smallTypeData[127] = 2;
+ multiple_map[2][1][2].a = 3;
+ multiple_map[2][1][2].finalArray.length = 4;
+ multiple_map[2][1][2].finalArray[3] = 5;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("data(uint256)", 0) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("data(uint256)", 8) == encodeArgs());
+ BOOST_CHECK(callContractFunction("dynamicData(uint256)", 2) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("dynamicData(uint256)", 8) == encodeArgs());
+ BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 1) == encodeArgs(22));
+ BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 127) == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("smallTypeData(uint256)", 128) == encodeArgs());
+ BOOST_CHECK(callContractFunction("multiple_map(uint256,uint256,uint256)", 2, 1, 2) == encodeArgs(3));
+}
+
+BOOST_AUTO_TEST_CASE(accessors_mapping_for_array)
+{
+ char const* sourceCode = R"(
+ contract test {
+ mapping(uint => uint[8]) public data;
+ mapping(uint => uint[]) public dynamicData;
+ function test() {
+ data[2][2] = 8;
+ dynamicData[2].length = 3;
+ dynamicData[2][2] = 8;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("data(uint256,uint256)", 2, 2) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("data(uint256, 256)", 2, 8) == encodeArgs());
+ BOOST_CHECK(callContractFunction("dynamicData(uint256,uint256)", 2, 2) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("dynamicData(uint256,uint256)", 2, 8) == encodeArgs());
+}
+
+BOOST_AUTO_TEST_CASE(multiple_elementary_accessors)
+{
+ char const* sourceCode = "contract test {\n"
+ " uint256 public data;\n"
+ " bytes6 public name;\n"
+ " bytes32 public a_hash;\n"
+ " address public an_address;\n"
+ " function test() {\n"
+ " data = 8;\n"
+ " name = \"Celina\";\n"
+ " a_hash = sha3(123);\n"
+ " an_address = address(0x1337);\n"
+ " super_secret_data = 42;\n"
+ " }\n"
+ " uint256 super_secret_data;"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("data()") == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina"));
+ BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes(1, 0x7b))));
+ BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337))));
+ BOOST_CHECK(callContractFunction("super_secret_data()") == bytes());
+}
+
+BOOST_AUTO_TEST_CASE(complex_accessors)
+{
+ char const* sourceCode = R"(
+ contract test {
+ mapping(uint256 => bytes4) public to_string_map;
+ mapping(uint256 => bool) public to_bool_map;
+ mapping(uint256 => uint256) public to_uint_map;
+ mapping(uint256 => mapping(uint256 => uint256)) public to_multiple_map;
+ function test() {
+ to_string_map[42] = "24";
+ to_bool_map[42] = false;
+ to_uint_map[42] = 12;
+ to_multiple_map[42][23] = 31;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("to_string_map(uint256)", 42) == encodeArgs("24"));
+ BOOST_CHECK(callContractFunction("to_bool_map(uint256)", 42) == encodeArgs(false));
+ BOOST_CHECK(callContractFunction("to_uint_map(uint256)", 42) == encodeArgs(12));
+ BOOST_CHECK(callContractFunction("to_multiple_map(uint256,uint256)", 42, 23) == encodeArgs(31));
+}
+
+BOOST_AUTO_TEST_CASE(struct_accessor)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct Data { uint a; uint8 b; mapping(uint => uint) c; bool d; }
+ mapping(uint => Data) public data;
+ function test() {
+ data[7].a = 1;
+ data[7].b = 2;
+ data[7].c[0] = 3;
+ data[7].d = true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("data(uint256)", 7) == encodeArgs(1, 2, true));
+}
+
+BOOST_AUTO_TEST_CASE(balance)
+{
+ char const* sourceCode = "contract test {\n"
+ " function getBalance() returns (uint256 balance) {\n"
+ " return address(this).balance;\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode, 23);
+ BOOST_CHECK(callContractFunction("getBalance()") == encodeArgs(23));
+}
+
+BOOST_AUTO_TEST_CASE(blockchain)
+{
+ char const* sourceCode = "contract test {\n"
+ " function someInfo() returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
+ " value = msg.value;\n"
+ " coinbase = block.coinbase;\n"
+ " blockNumber = block.number;\n"
+ " }\n"
+ "}\n";
+ m_envInfo.setAuthor(Address(0x123));
+ m_envInfo.setNumber(7);
+ compileAndRun(sourceCode, 27);
+ BOOST_CHECK(callContractFunctionWithValue("someInfo()", 28) == encodeArgs(28, 0x123, 7));
+}
+
+BOOST_AUTO_TEST_CASE(msg_sig)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function foo(uint256 a) returns (bytes4 value) {
+ return msg.sig;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+}
+
+BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function boo() returns (bytes4 value) {
+ return msg.sig;
+ }
+ function foo(uint256 a) returns (bytes4 value) {
+ return boo();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunctionWithValue("foo(uint256)", 13) == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes())));
+}
+
+BOOST_AUTO_TEST_CASE(now)
+{
+ char const* sourceCode = "contract test {\n"
+ " function someInfo() returns (bool equal, uint val) {\n"
+ " equal = block.timestamp == now;\n"
+ " val = now;\n"
+ " }\n"
+ "}\n";
+ m_envInfo.setTimestamp(9);
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true, 9));
+}
+
+BOOST_AUTO_TEST_CASE(type_conversions_cleanup)
+{
+ // 22-byte integer converted to a contract (i.e. address, 20 bytes), converted to a 32 byte
+ // integer should drop the first two bytes
+ char const* sourceCode = R"(
+ contract Test {
+ function test() returns (uint ret) { return uint(address(Test(address(0x11223344556677889900112233445566778899001122)))); }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_REQUIRE(callContractFunction("test()") == bytes({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22}));
+}
+// fixed bytes to fixed bytes conversion tests
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_smaller_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToBytes(bytes4 input) returns (bytes2 ret) {
+ return bytes2(input);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToBytes(bytes4)", "abcd") == encodeArgs("ab"));
+}
+
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_greater_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToBytes(bytes2 input) returns (bytes4 ret) {
+ return bytes4(input);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToBytes(bytes2)", "ab") == encodeArgs("ab"));
+}
+
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_same_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToBytes(bytes4 input) returns (bytes4 ret) {
+ return bytes4(input);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToBytes(bytes4)", "abcd") == encodeArgs("abcd"));
+}
+
+// fixed bytes to uint conversion tests
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_same_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToUint(bytes32 s) returns (uint256 h) {
+ return uint(s);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToUint(bytes32)", string("abc2")) ==
+ encodeArgs(u256("0x6162633200000000000000000000000000000000000000000000000000000000")));
+}
+
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_same_min_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToUint(bytes1 s) returns (uint8 h) {
+ return uint8(s);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToUint(bytes1)", string("a")) ==
+ encodeArgs(u256("0x61")));
+}
+
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_smaller_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToUint(bytes4 s) returns (uint16 h) {
+ return uint16(s);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToUint(bytes4)", string("abcd")) ==
+ encodeArgs(u256("0x6364")));
+}
+
+BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_greater_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function bytesToUint(bytes4 s) returns (uint64 h) {
+ return uint64(s);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("bytesToUint(bytes4)", string("abcd")) ==
+ encodeArgs(u256("0x61626364")));
+}
+
+// uint fixed bytes conversion tests
+BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_same_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function uintToBytes(uint256 h) returns (bytes32 s) {
+ return bytes32(h);
+ }
+ })";
+ compileAndRun(sourceCode);
+ u256 a("0x6162630000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK(callContractFunction("uintToBytes(uint256)", a) == encodeArgs(a));
+}
+
+BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_same_min_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function UintToBytes(uint8 h) returns (bytes1 s) {
+ return bytes1(h);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("UintToBytes(uint8)", u256("0x61")) ==
+ encodeArgs(string("a")));
+}
+
+BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_smaller_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function uintToBytes(uint32 h) returns (bytes2 s) {
+ return bytes2(h);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("uintToBytes(uint32)",
+ u160("0x61626364")) == encodeArgs(string("cd")));
+}
+
+BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function UintToBytes(uint16 h) returns (bytes8 s) {
+ return bytes8(h);
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("UintToBytes(uint16)", u256("0x6162")) ==
+ encodeArgs(string("\0\0\0\0\0\0ab", 8)));
+}
+
+BOOST_AUTO_TEST_CASE(send_ether)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(address addr, uint amount) returns (uint ret) {\n"
+ " addr.send(amount);\n"
+ " return address(this).balance;\n"
+ " }\n"
+ "}\n";
+ u256 amount(130);
+ compileAndRun(sourceCode, amount + 1);
+ u160 address(23);
+ BOOST_CHECK(callContractFunction("a(address,uint256)", address, amount) == encodeArgs(1));
+ BOOST_CHECK_EQUAL(m_state.balance(address), amount);
+}
+
+BOOST_AUTO_TEST_CASE(log0)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() {\n"
+ " log0(1);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ callContractFunction("a()");
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(log1)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() {\n"
+ " log1(1, 2);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ callContractFunction("a()");
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(log2)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() {\n"
+ " log2(1, 2, 3);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ callContractFunction("a()");
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 2);
+ for (unsigned i = 0; i < 2; ++i)
+ BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
+}
+
+BOOST_AUTO_TEST_CASE(log3)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() {\n"
+ " log3(1, 2, 3, 4);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ callContractFunction("a()");
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 3);
+ for (unsigned i = 0; i < 3; ++i)
+ BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
+}
+
+BOOST_AUTO_TEST_CASE(log4)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a() {\n"
+ " log4(1, 2, 3, 4, 5);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ callContractFunction("a()");
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 4);
+ for (unsigned i = 0; i < 4; ++i)
+ BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
+}
+
+BOOST_AUTO_TEST_CASE(log_in_constructor)
+{
+ char const* sourceCode = "contract test {\n"
+ " function test() {\n"
+ " log1(1, 2);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ BOOST_CHECK_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
+ BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(suicide)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(address receiver) returns (uint ret) {\n"
+ " suicide(receiver);\n"
+ " return 10;\n"
+ " }\n"
+ "}\n";
+ u256 amount(130);
+ compileAndRun(sourceCode, amount);
+ u160 address(23);
+ BOOST_CHECK(callContractFunction("a(address)", address) == bytes());
+ BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
+ BOOST_CHECK_EQUAL(m_state.balance(address), amount);
+}
+
+BOOST_AUTO_TEST_CASE(selfdestruct)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(address receiver) returns (uint ret) {\n"
+ " selfdestruct(receiver);\n"
+ " return 10;\n"
+ " }\n"
+ "}\n";
+ u256 amount(130);
+ compileAndRun(sourceCode, amount);
+ u160 address(23);
+ BOOST_CHECK(callContractFunction("a(address)", address) == bytes());
+ BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
+ BOOST_CHECK_EQUAL(m_state.balance(address), amount);
+}
+
+BOOST_AUTO_TEST_CASE(sha3)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(bytes32 input) returns (bytes32 sha3hash) {\n"
+ " return sha3(input);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _x) -> u256
+ {
+ return dev::sha3(toBigEndian(_x));
+ };
+ testSolidityAgainstCpp("a(bytes32)", f, u256(4));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(5));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
+BOOST_AUTO_TEST_CASE(sha256)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(bytes32 input) returns (bytes32 sha256hash) {\n"
+ " return sha256(input);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _input) -> u256
+ {
+ return dev::sha256(dev::ref(toBigEndian(_input)));
+ };
+ testSolidityAgainstCpp("a(bytes32)", f, u256(4));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(5));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
+BOOST_AUTO_TEST_CASE(ripemd)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(bytes32 input) returns (bytes32 sha256hash) {\n"
+ " return ripemd160(input);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ auto f = [&](u256 const& _input) -> u256
+ {
+ return h256(dev::ripemd160(h256(_input).ref()), h256::AlignLeft); // This should be aligned right. i guess it's fixed elsewhere?
+ };
+ testSolidityAgainstCpp("a(bytes32)", f, u256(4));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(5));
+ testSolidityAgainstCpp("a(bytes32)", f, u256(-1));
+}
+
+BOOST_AUTO_TEST_CASE(ecrecover)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address addr) {\n"
+ " return ecrecover(h, v, r, s);\n"
+ " }\n"
+ "}\n";
+ compileAndRun(sourceCode);
+ u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c");
+ byte v = 28;
+ u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f");
+ u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549");
+ u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
+ BOOST_CHECK(callContractFunction("a(bytes32,uint8,bytes32,bytes32)", h, v, r, s) == encodeArgs(addr));
+}
+
+BOOST_AUTO_TEST_CASE(inter_contract_calls)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ function multiply(uint a, uint b) returns (uint c) {
+ return a * b;
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper(uint a, uint b) returns (uint c) {
+ return h.multiply(a, b);
+ }
+ function getHelper() returns (address haddress) {
+ return address(h);
+ }
+ function setHelper(address haddress) {
+ h = Helper(haddress);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ u256 a(3456789);
+ u256 b("0x282837623374623234aa74");
+ BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b));
+}
+
+BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ function sel(uint a, bool select, uint b) returns (uint c) {
+ if (select) return a; else return b;
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper(uint a, bool select, uint b) returns (uint c) {
+ return h.sel(a, select, b) * 3;
+ }
+ function getHelper() returns (address haddress) {
+ return address(h);
+ }
+ function setHelper(address haddress) {
+ h = Helper(haddress);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ u256 a(3456789);
+ u256 b("0x282837623374623234aa74");
+ BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, true, b) == encodeArgs(a * 3));
+ BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, false, b) == encodeArgs(b * 3));
+}
+
+BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ function getAddress() returns (address addr) {
+ return address(this);
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper() returns (address addr) {
+ return h.getAddress();
+ }
+ function getHelper() returns (address addr) {
+ return address(h);
+ }
+ function setHelper(address addr) {
+ h = Helper(addr);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ BOOST_REQUIRE(callContractFunction("callHelper()") == encodeArgs(c_helperAddress));
+}
+
+BOOST_AUTO_TEST_CASE(calls_to_this)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ function invoke(uint a, uint b) returns (uint c) {
+ return this.multiply(a, b, 10);
+ }
+ function multiply(uint a, uint b, uint8 c) returns (uint ret) {
+ return a * b + c;
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper(uint a, uint b) returns (uint ret) {
+ return h.invoke(a, b);
+ }
+ function getHelper() returns (address addr) {
+ return address(h);
+ }
+ function setHelper(address addr) {
+ h = Helper(addr);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ u256 a(3456789);
+ u256 b("0x282837623374623234aa74");
+ BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b + 10));
+}
+
+BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
+{
+ // note that a reference to another contract's function occupies two stack slots,
+ // so this tests correct stack slot allocation
+ char const* sourceCode = R"(
+ contract Helper {
+ function multiply(uint a, uint b) returns (uint c) {
+ return a * b;
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper(uint a, uint b) returns (uint c) {
+ var fu = h.multiply;
+ var y = 9;
+ var ret = fu(a, b);
+ return ret + y;
+ }
+ function getHelper() returns (address haddress) {
+ return address(h);
+ }
+ function setHelper(address haddress) {
+ h = Helper(haddress);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ u256 a(3456789);
+ u256 b("0x282837623374623234aa74");
+ BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == encodeArgs(a * b + 9));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ function invoke(bytes3 x, bool stop) returns (bytes4 ret) {
+ return x;
+ }
+ }
+ contract Main {
+ Helper h;
+ function callHelper(bytes2 x, bool stop) returns (bytes5 ret) {
+ return h.invoke(x, stop);
+ }
+ function getHelper() returns (address addr) {
+ return address(h);
+ }
+ function setHelper(address addr) {
+ h = Helper(addr);
+ }
+ })";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes());
+ BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress));
+ BOOST_CHECK(callContractFunction("callHelper(bytes2,bool)", string("\0a", 2), true) == encodeArgs(string("\0a\0\0\0", 5)));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_arguments_internal)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ bytes3 name;
+ bool flag;
+
+ function Helper(bytes3 x, bool f) {
+ name = x;
+ flag = f;
+ }
+ function getName() returns (bytes3 ret) { return name; }
+ function getFlag() returns (bool ret) { return flag; }
+ }
+ contract Main {
+ Helper h;
+ function Main() {
+ h = new Helper("abc", true);
+ }
+ function getFlag() returns (bool ret) { return h.getFlag(); }
+ function getName() returns (bytes3 ret) { return h.getName(); }
+ })";
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_CHECK(callContractFunction("getFlag()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("getName()") == encodeArgs("abc"));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_arguments_external)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ bytes3 name;
+ bool flag;
+
+ function Main(bytes3 x, bool f) {
+ name = x;
+ flag = f;
+ }
+ function getName() returns (bytes3 ret) { return name; }
+ function getFlag() returns (bool ret) { return flag; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main", encodeArgs("abc", true));
+ BOOST_CHECK(callContractFunction("getFlag()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("getName()") == encodeArgs("abc"));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_with_long_arguments)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ string public a;
+ string public b;
+
+ function Main(string _a, string _b) {
+ a = _a;
+ b = _b;
+ }
+ }
+ )";
+ string a = "01234567890123gabddunaouhdaoneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi45678907890123456789abcd123456787890123456789abcd90123456789012345678901234567890123456789aboneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi45678907890123456789abcd123456787890123456789abcd90123456789012345678901234567890123456789aboneudapcgadi4567890789012oneudapcgadi4567890789012oneudapcgadi45678907890123456789abcd123456787890123456789abcd90123456789012345678901234567890123456789aboneudapcgadi4567890789012cdef";
+ string b = "AUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PYAUTAHIACIANOTUHAOCUHAOEUNAOEHUNTHDYDHPYDRCPYDRSTITOEUBXHUDGO>PY";
+
+ compileAndRun(sourceCode, 0, "Main", encodeArgs(
+ u256(0x40),
+ u256(0x40 + 0x20 + ((a.length() + 31) / 32) * 32),
+ u256(a.length()),
+ a,
+ u256(b.length()),
+ b
+ ));
+ BOOST_CHECK(callContractFunction("a()") == encodeDyn(a));
+ BOOST_CHECK(callContractFunction("b()") == encodeDyn(b));
+}
+
+BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ bytes3 name;
+ bool flag;
+ function Test() {
+ setName("abc");
+ }
+ function getName() returns (bytes3 ret) { return name; }
+ function setName(bytes3 _name) private { name = _name; }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc"));
+}
+
+BOOST_AUTO_TEST_CASE(contracts_as_addresses)
+{
+ char const* sourceCode = R"(
+ contract helper {
+ }
+ contract test {
+ helper h;
+ function test() { h = new helper(); h.send(5); }
+ function getBalance() returns (uint256 myBalance, uint256 helperBalance) {
+ myBalance = this.balance;
+ helperBalance = h.balance;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 20);
+ BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5)));
+}
+
+BOOST_AUTO_TEST_CASE(gas_and_value_basic)
+{
+ char const* sourceCode = R"(
+ contract helper {
+ bool flag;
+ function getBalance() returns (uint256 myBalance) {
+ return this.balance;
+ }
+ function setFlag() { flag = true; }
+ function getFlag() returns (bool fl) { return flag; }
+ }
+ contract test {
+ helper h;
+ function test() { h = new helper(); }
+ function sendAmount(uint amount) returns (uint256 bal) {
+ return h.getBalance.value(amount)();
+ }
+ function outOfGas() returns (bool ret) {
+ h.setFlag.gas(2)(); // should fail due to OOG
+ return true;
+ }
+ function checkState() returns (bool flagAfter, uint myBal) {
+ flagAfter = h.getFlag();
+ myBal = this.balance;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 20);
+ BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5));
+ // call to helper should not succeed but amount should be transferred anyway
+ BOOST_REQUIRE(callContractFunction("outOfGas()", 5) == bytes());
+ BOOST_REQUIRE(callContractFunction("checkState()", 5) == encodeArgs(false, 20 - 5));
+}
+
+BOOST_AUTO_TEST_CASE(gas_for_builtin)
+{
+ char const* sourceCode = R"(
+ contract Contract {
+ function test(uint g) returns (bytes32 data, bool flag) {
+ data = ripemd160.gas(g)("abc");
+ flag = true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test(uint256)", 500) == bytes());
+ BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc000000000000000000000000"), true));
+}
+
+BOOST_AUTO_TEST_CASE(value_complex)
+{
+ char const* sourceCode = R"(
+ contract helper {
+ function getBalance() returns (uint256 myBalance) {
+ return this.balance;
+ }
+ }
+ contract test {
+ helper h;
+ function test() { h = new helper(); }
+ function sendAmount(uint amount) returns (uint256 bal) {
+ var x1 = h.getBalance.value(amount);
+ uint someStackElement = 20;
+ var x2 = x1.gas(1000);
+ return x2.value(amount + 3)();// overwrite value
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 20);
+ BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8));
+}
+
+BOOST_AUTO_TEST_CASE(value_insane)
+{
+ char const* sourceCode = R"(
+ contract helper {
+ function getBalance() returns (uint256 myBalance) {
+ return this.balance;
+ }
+ }
+ contract test {
+ helper h;
+ function test() { h = new helper(); }
+ function sendAmount(uint amount) returns (uint256 bal) {
+ var x1 = h.getBalance.value;
+ var x2 = x1(amount).gas;
+ var x3 = x2(1000).value;
+ return x3(amount + 3)();// overwrite value
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 20);
+ BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8));
+}
+
+BOOST_AUTO_TEST_CASE(value_for_constructor)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ bytes3 name;
+ bool flag;
+ function Helper(bytes3 x, bool f) {
+ name = x;
+ flag = f;
+ }
+ function getName() returns (bytes3 ret) { return name; }
+ function getFlag() returns (bool ret) { return flag; }
+ }
+ contract Main {
+ Helper h;
+ function Main() {
+ h = (new Helper).value(10)("abc", true);
+ }
+ function getFlag() returns (bool ret) { return h.getFlag(); }
+ function getName() returns (bytes3 ret) { return h.getName(); }
+ function getBalances() returns (uint me, uint them) { me = this.balance; them = h.balance;}
+ })";
+ compileAndRun(sourceCode, 22, "Main");
+ BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc"));
+ BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10));
+}
+
+BOOST_AUTO_TEST_CASE(virtual_function_calls)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ function f() returns (uint i) { return g(); }
+ function g() returns (uint i) { return 1; }
+ }
+ contract Derived is Base {
+ function g() returns (uint i) { return 2; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(access_base_storage)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ uint dataBase;
+ function getViaBase() returns (uint i) { return dataBase; }
+ }
+ contract Derived is Base {
+ uint dataDerived;
+ function setData(uint base, uint derived) returns (bool r) {
+ dataBase = base;
+ dataDerived = derived;
+ return true;
+ }
+ function getViaDerived() returns (uint base, uint derived) {
+ base = dataBase;
+ derived = dataDerived;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("setData(uint256,uint256)", 1, 2) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("getViaBase()") == encodeArgs(1));
+ BOOST_CHECK(callContractFunction("getViaDerived()") == encodeArgs(1, 2));
+}
+
+BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ uint data;
+ function setData(uint i) { data = i; }
+ function getViaBase() returns (uint i) { return data; }
+ }
+ contract A is Base { function setViaA(uint i) { setData(i); } }
+ contract B is Base { function getViaB() returns (uint i) { return getViaBase(); } }
+ contract Derived is Base, B, A { }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("setViaA(uint256)", 23) == encodeArgs());
+ BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(23));
+}
+
+BOOST_AUTO_TEST_CASE(explicit_base_class)
+{
+ char const* sourceCode = R"(
+ contract BaseBase { function g() returns (uint r) { return 1; } }
+ contract Base is BaseBase { function g() returns (uint r) { return 2; } }
+ contract Derived is Base {
+ function f() returns (uint r) { return BaseBase.g(); }
+ function g() returns (uint r) { return 3; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(3));
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(base_constructor_arguments)
+{
+ char const* sourceCode = R"(
+ contract BaseBase {
+ uint m_a;
+ function BaseBase(uint a) {
+ m_a = a;
+ }
+ }
+ contract Base is BaseBase(7) {
+ function Base() {
+ m_a *= m_a;
+ }
+ }
+ contract Derived is Base() {
+ function getA() returns (uint r) { return m_a; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("getA()") == encodeArgs(7 * 7));
+}
+
+BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments)
+{
+ char const* sourceCode = R"(
+ contract BaseBase {
+ uint m_a;
+ function BaseBase(uint a) {
+ m_a = a;
+ }
+ function g() returns (uint r) { return 2; }
+ }
+ contract Base is BaseBase(BaseBase.g()) {
+ }
+ contract Derived is Base() {
+ function getA() returns (uint r) { return m_a; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("getA()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(virtual_function_usage_in_constructor_arguments)
+{
+ char const* sourceCode = R"(
+ contract BaseBase {
+ uint m_a;
+ function BaseBase(uint a) {
+ m_a = a;
+ }
+ function overridden() returns (uint r) { return 1; }
+ function g() returns (uint r) { return overridden(); }
+ }
+ contract Base is BaseBase(BaseBase.g()) {
+ }
+ contract Derived is Base() {
+ function getA() returns (uint r) { return m_a; }
+ function overridden() returns (uint r) { return 2; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("getA()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_argument_overriding)
+{
+ char const* sourceCode = R"(
+ contract BaseBase {
+ uint m_a;
+ function BaseBase(uint a) {
+ m_a = a;
+ }
+ }
+ contract Base is BaseBase(2) { }
+ contract Derived is BaseBase(3), Base {
+ function getA() returns (uint r) { return m_a; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Derived");
+ BOOST_CHECK(callContractFunction("getA()") == encodeArgs(3));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function getOne() nonFree returns (uint r) { return 1; }
+ modifier nonFree { if (msg.value > 0) _ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getOne()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunctionWithValue("getOne()", 1) == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_local_variables)
+{
+ char const* sourceCode = R"(
+ contract C {
+ modifier mod1 { var a = 1; var b = 2; _ }
+ modifier mod2(bool a) { if (a) return; else _ }
+ function f(bool a) mod1 mod2(a) returns (uint r) { return 3; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(3));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_loop)
+{
+ char const* sourceCode = R"(
+ contract C {
+ modifier repeat(uint count) { for (var i = 0; i < count; ++i) _ }
+ function f() repeat(10) returns (uint r) { r += 1; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(10));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation)
+{
+ char const* sourceCode = R"(
+ contract C {
+ modifier repeat(bool twice) { if (twice) _ _ }
+ function f(bool twice) repeat(twice) returns (uint r) { r += 1; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(1));
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return)
+{
+ // Here, the explicit return prevents the second execution
+ char const* sourceCode = R"(
+ contract C {
+ modifier repeat(bool twice) { if (twice) _ _ }
+ function f(bool twice) repeat(twice) returns (uint r) { r += 1; return r; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(1));
+ BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_overriding)
+{
+ char const* sourceCode = R"(
+ contract A {
+ function f() mod returns (bool r) { return true; }
+ modifier mod { _ }
+ }
+ contract C is A {
+ modifier mod { }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint data;
+ function A() mod1 { f1(); }
+ function f1() mod2 { data |= 0x1; }
+ function f2() { data |= 0x20; }
+ function f3() { }
+ modifier mod1 { f2(); _ }
+ modifier mod2 { f3(); }
+ function getData() returns (uint r) { return data; }
+ }
+ contract C is A {
+ modifier mod1 { f4(); _ }
+ function f3() { data |= 0x300; }
+ function f4() { data |= 0x4000; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0x4300));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_for_constructor)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint data;
+ function A() mod1 { data |= 2; }
+ modifier mod1 { data |= 1; _ }
+ function getData() returns (uint r) { return data; }
+ }
+ contract C is A {
+ modifier mod1 { data |= 4; _ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(4 | 2));
+}
+
+BOOST_AUTO_TEST_CASE(use_std_lib)
+{
+ char const* sourceCode = R"(
+ import "mortal";
+ contract Icarus is mortal { }
+ )";
+ m_addStandardSources = true;
+ u256 amount(130);
+ u160 address(23);
+ compileAndRun(sourceCode, amount, "Icarus");
+ u256 balanceBefore = m_state.balance(m_sender);
+ BOOST_CHECK(callContractFunction("kill()") == bytes());
+ BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
+ BOOST_CHECK(m_state.balance(m_sender) > balanceBefore);
+}
+
+BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint r) {
+ uint; uint; uint; uint;
+ int x = -7;
+ var a = uint;
+ return a(x);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(-7)));
+}
+
+BOOST_AUTO_TEST_CASE(super)
+{
+ char const* sourceCode = R"(
+ contract A { function f() returns (uint r) { return 1; } }
+ contract B is A { function f() returns (uint r) { return super.f() | 2; } }
+ contract C is A { function f() returns (uint r) { return super.f() | 4; } }
+ contract D is B, C { function f() returns (uint r) { return super.f() | 8; } }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8));
+}
+
+BOOST_AUTO_TEST_CASE(super_in_constructor)
+{
+ char const* sourceCode = R"(
+ contract A { function f() returns (uint r) { return 1; } }
+ contract B is A { function f() returns (uint r) { return super.f() | 2; } }
+ contract C is A { function f() returns (uint r) { return super.f() | 4; } }
+ contract D is B, C { uint data; function D() { data = super.f() | 8; } function f() returns (uint r) { return data; } }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8));
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint data;
+ function() returns (uint r) { data = 1; return 2; }
+ function getData() returns (uint r) { return data; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("") == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(inherited_fallback_function)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint data;
+ function() returns (uint r) { data = 1; return 2; }
+ function getData() returns (uint r) { return data; }
+ }
+ contract B is A {}
+ )";
+ compileAndRun(sourceCode, 0, "B");
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("") == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("getData()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(event)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
+ function deposit(bytes32 _id, bool _manually) {
+ if (_manually) {
+ bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f;
+ log3(bytes32(msg.value), s, bytes32(msg.sender), _id);
+ } else
+ Deposit(msg.sender, _id, msg.value);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 value(18);
+ u256 id(0x1234);
+ for (bool manually: {true, false})
+ {
+ callContractFunctionWithValue("deposit(bytes32,bool)", value, id, manually);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value)));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256)")));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(event_no_arguments)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit;
+ function deposit() {
+ Deposit();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data.empty());
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit()")));
+}
+
+BOOST_AUTO_TEST_CASE(event_anonymous)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit() anonymous;
+ function deposit() {
+ Deposit();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(event_anonymous_with_topics)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous;
+ function deposit(bytes32 _id, bool _manually) {
+ Deposit(msg.sender, _id, msg.value, 2, "abc");
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 value(18);
+ u256 id(0x1234);
+ callContractFunctionWithValue("deposit(bytes32,bool)", value, id);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs("abc"));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 4);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(m_sender));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(id));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(value));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[3], h256(2));
+}
+
+BOOST_AUTO_TEST_CASE(event_lots_of_data)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit(address _from, bytes32 _id, uint _value, bool _flag);
+ function deposit(bytes32 _id) {
+ Deposit(msg.sender, _id, msg.value, true);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 value(18);
+ u256 id(0x1234);
+ callContractFunctionWithValue("deposit(bytes32)", value, id);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs((u160)m_sender, id, value, true));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256,bool)")));
+}
+
+BOOST_AUTO_TEST_CASE(event_really_lots_of_data)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ event Deposit(uint fixeda, bytes dynx, uint fixedb);
+ function deposit() {
+ Deposit(10, msg.data, 15);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes());
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)")));
+}
+
+BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage)
+{
+ char const* sourceCode = R"(
+ contract ClientReceipt {
+ bytes x;
+ event Deposit(uint fixeda, bytes dynx, uint fixedb);
+ function deposit() {
+ x.length = 3;
+ x[0] = "A";
+ x[1] = "B";
+ x[2] = "C";
+ Deposit(10, x, 15);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC")));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)")));
+}
+
+BOOST_AUTO_TEST_CASE(event_indexed_string)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string x;
+ uint[4] y;
+ event E(string indexed r, uint[4] indexed t);
+ function deposit() {
+ bytes(x).length = 90;
+ for (uint i = 0; i < 90; i++)
+ bytes(x)[i] = byte(i);
+ y[0] = 4;
+ y[1] = 5;
+ y[2] = 6;
+ y[3] = 7;
+ E(x, y);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ callContractFunction("deposit()");
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ string dynx(90, 0);
+ for (size_t i = 0; i < dynx.size(); ++i)
+ dynx[i] = i;
+ BOOST_CHECK(m_logs[0].data == bytes());
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[1], dev::sha3(dynx));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[2], dev::sha3(
+ encodeArgs(u256(4), u256(5), u256(6), u256(7))
+ ));
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("E(string,uint256[4])")));
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint, uint k) returns(uint ret_k, uint ret_g){
+ uint g = 8;
+ ret_k = k;
+ ret_g = g;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) == encodeArgs(9, 8));
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint k) returns(uint){
+ return k;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256)", 9) == encodeArgs(9));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_multiple_arguments)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function foo(uint a, uint b, uint c) returns (bytes32 d)
+ {
+ d = sha3(a, b, c);
+ }
+ })";
+ compileAndRun(sourceCode);
+
+ BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs(
+ dev::sha3(
+ toBigEndian(u256(10)) +
+ toBigEndian(u256(12)) +
+ toBigEndian(u256(13)))));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function foo(uint a, uint16 b) returns (bytes32 d)
+ {
+ d = sha3(a, b, 145);
+ }
+ })";
+ compileAndRun(sourceCode);
+
+ BOOST_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12) == encodeArgs(
+ dev::sha3(
+ toBigEndian(u256(10)) +
+ bytes{0x0, 0xc} +
+ bytes(1, 0x91))));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function foo() returns (bytes32 d)
+ {
+ d = sha3("foo");
+ }
+ function bar(uint a, uint16 b) returns (bytes32 d)
+ {
+ d = sha3(a, b, 145, "foo");
+ }
+ })";
+ compileAndRun(sourceCode);
+
+ BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::sha3("foo")));
+
+ BOOST_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12) == encodeArgs(
+ dev::sha3(
+ toBigEndian(u256(10)) +
+ bytes{0x0, 0xc} +
+ bytes(1, 0x91) +
+ bytes{0x66, 0x6f, 0x6f})));
+}
+
+BOOST_AUTO_TEST_CASE(sha3_with_bytes)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function foo() returns (bool)
+ {
+ data.length = 3;
+ data[0] = "f";
+ data[1] = "o";
+ data[2] = "o";
+ return sha3(data) == sha3("foo");
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function foo() returns (bytes32)
+ {
+ data.length = 3;
+ data[0] = "x";
+ data[1] = "y";
+ data[2] = "z";
+ return sha3("b", sha3(data), "a");
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("foo()") == encodeArgs(
+ u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'}))
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(generic_call)
+{
+ char const* sourceCode = R"**(
+ contract receiver {
+ uint public received;
+ function receive(uint256 x) { received = x; }
+ }
+ contract sender {
+ function doSend(address rec) returns (uint d)
+ {
+ bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
+ rec.call.value(2)(signature, 23);
+ return receiver(rec).received();
+ }
+ }
+ )**";
+ compileAndRun(sourceCode, 0, "receiver");
+ u160 const c_receiverAddress = m_contractAddress;
+ compileAndRun(sourceCode, 50, "sender");
+ BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23));
+ BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2);
+}
+
+BOOST_AUTO_TEST_CASE(generic_callcode)
+{
+ char const* sourceCode = R"**(
+ contract receiver {
+ uint public received;
+ function receive(uint256 x) { received = x; }
+ }
+ contract sender {
+ uint public received;
+ function doSend(address rec) returns (uint d)
+ {
+ bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
+ rec.callcode.value(2)(signature, 23);
+ return receiver(rec).received();
+ }
+ }
+ )**";
+ compileAndRun(sourceCode, 0, "receiver");
+ u160 const c_receiverAddress = m_contractAddress;
+ compileAndRun(sourceCode, 50, "sender");
+ u160 const c_senderAddress = m_contractAddress;
+ BOOST_CHECK(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("received()") == encodeArgs(23));
+ m_contractAddress = c_receiverAddress;
+ BOOST_CHECK(callContractFunction("received()") == encodeArgs(0));
+ BOOST_CHECK(m_state.storage(c_receiverAddress).empty());
+ BOOST_CHECK(!m_state.storage(c_senderAddress).empty());
+ BOOST_CHECK_EQUAL(m_state.balance(c_receiverAddress), 0);
+ BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50);
+}
+
+BOOST_AUTO_TEST_CASE(generic_delegatecall)
+{
+ char const* sourceCode = R"**(
+ contract receiver {
+ uint public received;
+ address public sender;
+ uint public value;
+ function receive(uint256 x) { received = x; sender = msg.sender; value = msg.value; }
+ }
+ contract sender {
+ uint public received;
+ address public sender;
+ uint public value;
+ function doSend(address rec)
+ {
+ bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
+ rec.delegatecall(signature, 23);
+ }
+ }
+ )**";
+ compileAndRun(sourceCode, 0, "receiver");
+ u160 const c_receiverAddress = m_contractAddress;
+ compileAndRun(sourceCode, 50, "sender");
+ u160 const c_senderAddress = m_contractAddress;
+ BOOST_CHECK(m_sender != c_senderAddress); // just for sanity
+ BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs());
+ BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(23)));
+ BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender)));
+ BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(11)));
+ m_contractAddress = c_receiverAddress;
+ BOOST_CHECK(callContractFunction("received()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("value()") == encodeArgs(u256(0)));
+ BOOST_CHECK(m_state.storage(c_receiverAddress).empty());
+ BOOST_CHECK(!m_state.storage(c_senderAddress).empty());
+ BOOST_CHECK_EQUAL(m_state.balance(c_receiverAddress), 0);
+ BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50 + 11);
+}
+
+BOOST_AUTO_TEST_CASE(library_call_in_homestead)
+{
+ char const* sourceCode = R"(
+ library Lib { function m() returns (address) { return msg.sender; } }
+ contract Test {
+ address public sender;
+ function f() {
+ sender = Lib.m();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("sender()") == encodeArgs(u160(m_sender)));
+}
+
+BOOST_AUTO_TEST_CASE(store_bytes)
+{
+ // this test just checks that the copy loop does not mess up the stack
+ char const* sourceCode = R"(
+ contract C {
+ function save() returns (uint r) {
+ r = 23;
+ savedData = msg.data;
+ r = 24;
+ }
+ bytes savedData;
+ }
+ )";
+ compileAndRun(sourceCode);
+ // empty copy loop
+ BOOST_CHECK(callContractFunction("save()") == encodeArgs(24));
+ BOOST_CHECK(callContractFunction("save()", "abcdefg") == encodeArgs(24));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function() returns (bytes32) {
+ return sha3("abc", msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ bytes calldata1 = bytes(61, 0x22) + bytes(12, 0x12);
+ sendMessage(calldata1, false);
+ BOOST_CHECK(m_output == encodeArgs(dev::sha3(bytes{'a', 'b', 'c'} + calldata1)));
+}
+
+BOOST_AUTO_TEST_CASE(call_forward_bytes)
+{
+ char const* sourceCode = R"(
+ contract receiver {
+ uint public received;
+ function receive(uint x) { received += x + 1; }
+ function() { received = 0x80; }
+ }
+ contract sender {
+ function sender() { rec = new receiver(); }
+ function() { savedData = msg.data; }
+ function forward() returns (bool) { rec.call(savedData); return true; }
+ function clear() returns (bool) { delete savedData; return true; }
+ function val() returns (uint) { return rec.received(); }
+ receiver rec;
+ bytes savedData;
+ }
+ )";
+ compileAndRun(sourceCode, 0, "sender");
+ BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("clear()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("forward()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
+}
+
+BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
+{
+ char const* sourceCode = R"(
+ contract receiver {
+ uint public received;
+ function receive(uint x) { received += x + 1; }
+ function() { received = 0x80; }
+ }
+ contract sender {
+ function sender() { rec = new receiver(); }
+ function() { savedData1 = savedData2 = msg.data; }
+ function forward(bool selector) returns (bool) {
+ if (selector) { rec.call(savedData1); delete savedData1; }
+ else { rec.call(savedData2); delete savedData2; }
+ return true;
+ }
+ function val() returns (uint) { return rec.received(); }
+ receiver rec;
+ bytes savedData1;
+ bytes savedData2;
+ }
+ )";
+ compileAndRun(sourceCode, 0, "sender");
+ BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("forward(bool)", false) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(16));
+ BOOST_CHECK(callContractFunction("forward(bool)", true) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
+}
+
+BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function() { data = msg.data; }
+ function del() returns (bool) { delete data; return true; }
+ bytes data;
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("---", 7) == bytes());
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("del()", 7) == encodeArgs(true));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function set() returns (bool) { data = msg.data; return true; }
+ function() { data = msg.data; }
+ bytes data;
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ sendMessage(bytes(), false);
+ BOOST_CHECK(m_output == bytes());
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function set() returns (bool) { data1 = msg.data; return true; }
+ function reset() returns (bool) { data1 = data2; return true; }
+ bytes data1;
+ bytes data2;
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("reset()") == encodeArgs(true));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(bytes_inside_mappings)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function set(uint key) returns (bool) { data[key] = msg.data; return true; }
+ function copy(uint from, uint to) returns (bool) { data[to] = data[from]; return true; }
+ mapping(uint => bytes) data;
+ }
+ )";
+ compileAndRun(sourceCode);
+ // store a short byte array at 1 and a longer one at 2
+ BOOST_CHECK(callContractFunction("set(uint256)", 1, 2) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ // copy shorter to longer
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ // copy empty to both
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2) == encodeArgs(true));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(bytes_length_member)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function set() returns (bool) { data = msg.data; return true; }
+ function getLength() returns (uint) { return data.length; }
+ bytes data;
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("set()", 1, 2) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(4+32+32));
+}
+
+BOOST_AUTO_TEST_CASE(struct_copy)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Nested { uint x; uint y; }
+ struct Struct { uint a; mapping(uint => Struct) b; Nested nested; uint c; }
+ mapping(uint => Struct) data;
+ function set(uint k) returns (bool) {
+ data[k].a = 1;
+ data[k].nested.x = 3;
+ data[k].nested.y = 4;
+ data[k].c = 2;
+ return true;
+ }
+ function copy(uint from, uint to) returns (bool) {
+ data[to] = data[from];
+ return true;
+ }
+ function retrieve(uint k) returns (uint a, uint x, uint y, uint c)
+ {
+ a = data[k].a;
+ x = data[k].nested.x;
+ y = data[k].nested.y;
+ c = data[k].c;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("set(uint256)", 7) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(1, 3, 4, 2));
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(1, 3, 4, 2));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(1, 3, 4, 2));
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 0, 7) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(0, 0, 0, 0));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(1, 3, 4, 2));
+ BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(0, 0, 0, 0));
+}
+
+BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Struct { uint a; bytes data; uint b; }
+ Struct data1;
+ Struct data2;
+ function set(uint _a, bytes _data, uint _b) external returns (bool) {
+ data1.a = _a;
+ data1.b = _b;
+ data1.data = _data;
+ return true;
+ }
+ function copy() returns (bool) {
+ data1 = data2;
+ return true;
+ }
+ function del() returns (bool) {
+ delete data1;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ string data = "123456789012345678901234567890123";
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, u256(data.length()), 13, data) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("copy()") == encodeArgs(true));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, u256(data.length()), 13, data) == encodeArgs(true));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("del()") == encodeArgs(true));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(struct_copy_via_local)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Struct { uint a; uint b; }
+ Struct data1;
+ Struct data2;
+ function test() returns (bool) {
+ data1.a = 1;
+ data1.b = 2;
+ var x = data1;
+ data2 = x;
+ return data2.a == data1.a && data2.b == data1.b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(using_enums)
+{
+ char const* sourceCode = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ choices = ActionChoices.GoStraight;
+ }
+ function getChoice() returns (uint d)
+ {
+ d = uint256(choices);
+ }
+ ActionChoices choices;
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(constructing_enums_from_ints)
+{
+ char const* sourceCode = R"(
+ contract c {
+ enum Truth { False, True }
+ function test() returns (uint)
+ {
+ return uint(Truth(uint8(0x701)));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(inline_member_init)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test(){
+ m_b = 6;
+ m_c = 8;
+ }
+ uint m_a = 5;
+ uint m_b;
+ uint m_c = 7;
+ function get() returns (uint a, uint b, uint c){
+ a = m_a;
+ b = m_b;
+ c = m_c;
+ }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(5, 6, 8));
+}
+
+BOOST_AUTO_TEST_CASE(inline_member_init_inheritence)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ function Base(){}
+ uint m_base = 5;
+ function getBMember() returns (uint i) { return m_base; }
+ }
+ contract Derived is Base {
+ function Derived(){}
+ uint m_derived = 6;
+ function getDMember() returns (uint i) { return m_derived; }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getBMember()") == encodeArgs(5));
+ BOOST_CHECK(callContractFunction("getDMember()") == encodeArgs(6));
+}
+
+BOOST_AUTO_TEST_CASE(inline_member_init_inheritence_without_constructor)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ uint m_base = 5;
+ function getBMember() returns (uint i) { return m_base; }
+ }
+ contract Derived is Base {
+ uint m_derived = 6;
+ function getDMember() returns (uint i) { return m_derived; }
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getBMember()") == encodeArgs(5));
+ BOOST_CHECK(callContractFunction("getDMember()") == encodeArgs(6));
+}
+
+BOOST_AUTO_TEST_CASE(external_function)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function f(uint a) returns (uint) { return a; }
+ function test(uint a, uint b) external returns (uint r_a, uint r_b) {
+ r_a = f(a + 7);
+ r_b = b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test(uint256,uint256)", 2, 3) == encodeArgs(2+7, 3));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_in_arguments)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint result;
+ function f(uint a, uint b) { result += a + b; }
+ function g(uint a) { result *= a; }
+ function test(uint a, bytes data1, bytes data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) {
+ r_a = a;
+ this.call(data1);
+ this.call(data2);
+ r = result;
+ r_b = b;
+ l = data1.length;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+
+ string innercalldata1 = asString(FixedHash<4>(dev::sha3("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9));
+ string innercalldata2 = asString(FixedHash<4>(dev::sha3("g(uint256)")).asBytes() + encodeArgs(3));
+ bytes calldata = encodeArgs(
+ 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13,
+ u256(innercalldata1.length()), innercalldata1,
+ u256(innercalldata2.length()), innercalldata2);
+ BOOST_CHECK(callContractFunction("test(uint256,bytes,bytes,uint256)", calldata)
+ == encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length())));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Data { uint x; uint y; }
+ Data[2**10] data;
+ uint[2**10 + 3] ids;
+ function setIDStatic(uint id) { ids[2] = id; }
+ function setID(uint index, uint id) { ids[index] = id; }
+ function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; }
+ function getID(uint index) returns (uint) { return ids[index]; }
+ function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; }
+ function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("setIDStatic(uint256)", 11) == bytes());
+ BOOST_CHECK(callContractFunction("getID(uint256)", 2) == encodeArgs(11));
+ BOOST_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8) == bytes());
+ BOOST_CHECK(callContractFunction("getID(uint256)", 7) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9) == bytes());
+ BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11) == bytes());
+ BOOST_CHECK(callContractFunction("getData(uint256)", 7) == encodeArgs(8, 9));
+ BOOST_CHECK(callContractFunction("getData(uint256)", 8) == encodeArgs(10, 11));
+ BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(u256(1) << 10, (u256(1) << 10) + 3));
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Data { uint x; uint y; }
+ Data[] data;
+ uint[] ids;
+ function setIDStatic(uint id) { ids[2] = id; }
+ function setID(uint index, uint id) { ids[index] = id; }
+ function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; }
+ function getID(uint index) returns (uint) { return ids[index]; }
+ function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; }
+ function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; }
+ function setLengths(uint l1, uint l2) { data.length = l1; ids.length = l2; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(0, 0));
+ BOOST_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49) == bytes());
+ BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(48, 49));
+ BOOST_CHECK(callContractFunction("setIDStatic(uint256)", 11) == bytes());
+ BOOST_CHECK(callContractFunction("getID(uint256)", 2) == encodeArgs(11));
+ BOOST_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8) == bytes());
+ BOOST_CHECK(callContractFunction("getID(uint256)", 7) == encodeArgs(8));
+ BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9) == bytes());
+ BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11) == bytes());
+ BOOST_CHECK(callContractFunction("getData(uint256)", 7) == encodeArgs(8, 9));
+ BOOST_CHECK(callContractFunction("getData(uint256)", 8) == encodeArgs(10, 11));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_out_of_bounds_array_access)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[4] data;
+ function set(uint index, uint value) returns (bool) { data[index] = value; return true; }
+ function get(uint index) returns (uint) { return data[index]; }
+ function length() returns (uint) { return data.length; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("set(uint256,uint256)", 3, 4) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("set(uint256,uint256)", 4, 5) == bytes());
+ BOOST_CHECK(callContractFunction("set(uint256,uint256)", 400, 5) == bytes());
+ BOOST_CHECK(callContractFunction("get(uint256)", 3) == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("get(uint256)", 4) == bytes());
+ BOOST_CHECK(callContractFunction("get(uint256)", 400) == bytes());
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(4));
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[] data;
+ function enlarge(uint amount) returns (uint) { return data.length += amount; }
+ function set(uint index, uint value) returns (bool) { data[index] = value; return true; }
+ function get(uint index) returns (uint) { return data[index]; }
+ function length() returns (uint) { return data.length; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(0));
+ BOOST_CHECK(callContractFunction("get(uint256)", 3) == bytes());
+ BOOST_CHECK(callContractFunction("enlarge(uint256)", 4) == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("set(uint256,uint256)", 3, 4) == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("get(uint256)", 3) == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(4));
+ BOOST_CHECK(callContractFunction("set(uint256,uint256)", 4, 8) == bytes());
+ BOOST_CHECK(callContractFunction("length()") == encodeArgs(4));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint spacer1;
+ uint spacer2;
+ uint[20] data;
+ function fill() {
+ for (uint i = 0; i < data.length; ++i) data[i] = i+1;
+ }
+ function clear() { delete data; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("fill()") == bytes());
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("clear()") == bytes());
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint spacer1;
+ uint spacer2;
+ uint[3] data;
+ function fill() {
+ for (uint i = 0; i < data.length; ++i) data[i] = i+1;
+ }
+ function clear() { delete data; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("fill()") == bytes());
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("clear()") == bytes());
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[20] spacer;
+ uint[] dynamic;
+ function fill() {
+ dynamic.length = 21;
+ for (uint i = 0; i < dynamic.length; ++i) dynamic[i] = i+1;
+ }
+ function halfClear() { dynamic.length = 5; }
+ function fullClear() { delete dynamic; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("fill()") == bytes());
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("halfClear()") == bytes());
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("fullClear()") == bytes());
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct s { uint[][] d; }
+ s[] data;
+ function fill() returns (uint) {
+ data.length = 3;
+ data[2].d.length = 4;
+ data[2].d[3].length = 5;
+ data[2].d[3][4] = 8;
+ return data[2].d[3][4];
+ }
+ function clear() { delete data; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("fill()") == encodeArgs(8));
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("clear()") == bytes());
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[] data1;
+ uint[] data2;
+ function setData1(uint length, uint index, uint value) {
+ data1.length = length; if (index < length) data1[index] = value;
+ }
+ function copyStorageStorage() { data2 = data1; }
+ function getData2(uint index) returns (uint len, uint val) {
+ len = data2.length; if (index < len) val = data2[index];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4) == bytes());
+ BOOST_CHECK(callContractFunction("copyStorageStorage()") == bytes());
+ BOOST_CHECK(callContractFunction("getData2(uint256)", 5) == encodeArgs(10, 4));
+ BOOST_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0) == bytes());
+ BOOST_CHECK(callContractFunction("copyStorageStorage()") == bytes());
+ BOOST_CHECK(callContractFunction("getData2(uint256)", 0) == encodeArgs(0, 0));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_static)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[40] data1;
+ uint[20] data2;
+ function test() returns (uint x, uint y){
+ data1[30] = 4;
+ data1[2] = 7;
+ data1[3] = 9;
+ data2[3] = 8;
+ data1 = data2;
+ x = data1[3];
+ y = data1[30]; // should be cleared
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(8, 0));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_dynamic)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[9] data1;
+ uint[] data2;
+ function test() returns (uint x, uint y){
+ data1[8] = 4;
+ data2 = data1;
+ x = data2.length;
+ y = data2[8];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(9, 4));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_different_packing)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes8[] data1; // 4 per slot
+ bytes10[] data2; // 3 per slot
+ function test() returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) {
+ data1.length = 9;
+ for (uint i = 0; i < data1.length; ++i)
+ data1[i] = bytes8(i);
+ data2 = data1;
+ a = data2[1];
+ b = data2[2];
+ c = data2[3];
+ d = data2[4];
+ e = data2[5];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(
+ asString(fromHex("0000000000000001")),
+ asString(fromHex("0000000000000002")),
+ asString(fromHex("0000000000000003")),
+ asString(fromHex("0000000000000004")),
+ asString(fromHex("0000000000000005"))
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_target_simple)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes8[9] data1; // 4 per slot
+ bytes17[10] data2; // 1 per slot, no offset counter
+ function test() returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) {
+ for (uint i = 0; i < data1.length; ++i)
+ data1[i] = bytes8(i);
+ data2[8] = data2[9] = 2;
+ data2 = data1;
+ a = data2[1];
+ b = data2[2];
+ c = data2[3];
+ d = data2[4];
+ e = data2[9];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(
+ asString(fromHex("0000000000000001")),
+ asString(fromHex("0000000000000002")),
+ asString(fromHex("0000000000000003")),
+ asString(fromHex("0000000000000004")),
+ asString(fromHex("0000000000000000"))
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_target_leftover)
+{
+ // test that leftover elements in the last slot of target are correctly cleared during assignment
+ char const* sourceCode = R"(
+ contract c {
+ byte[10] data1;
+ bytes2[32] data2;
+ function test() returns (uint check, uint res1, uint res2) {
+ uint i;
+ for (i = 0; i < data2.length; ++i)
+ data2[i] = 0xffff;
+ check = uint(data2[31]) * 0x10000 | uint(data2[14]);
+ for (i = 0; i < data1.length; ++i)
+ data1[i] = byte(uint8(1 + i));
+ data2 = data1;
+ for (i = 0; i < 16; ++i)
+ res1 |= uint(data2[i]) * 0x10000**i;
+ for (i = 0; i < 16; ++i)
+ res2 |= uint(data2[16 + i]) * 0x10000**i;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(
+ u256("0xffffffff"),
+ asString(fromHex("0000000000000000""000000000a000900""0800070006000500""0400030002000100")),
+ asString(fromHex("0000000000000000""0000000000000000""0000000000000000""0000000000000000"))
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_target_leftover2)
+{
+ // since the copy always copies whole slots, we have to make sure that the source size maxes
+ // out a whole slot and at the same time there are still elements left in the target at that point
+ char const* sourceCode = R"(
+ contract c {
+ bytes8[4] data1; // fits into one slot
+ bytes10[6] data2; // 4 elements need two slots
+ function test() returns (bytes10 r1, bytes10 r2, bytes10 r3) {
+ data1[0] = 1;
+ data1[1] = 2;
+ data1[2] = 3;
+ data1[3] = 4;
+ for (uint i = 0; i < data2.length; ++i)
+ data2[i] = bytes10(0xffff00 | (1 + i));
+ data2 = data1;
+ r1 = data2[3];
+ r2 = data2[4];
+ r3 = data2[5];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(
+ asString(fromHex("0000000000000004")),
+ asString(fromHex("0000000000000000")),
+ asString(fromHex("0000000000000000"))
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct Data { uint x; uint y; }
+ Data[] data1;
+ Data[] data2;
+ function test() returns (uint x, uint y) {
+ data1.length = 9;
+ data1[8].x = 4;
+ data1[8].y = 5;
+ data2 = data1;
+ x = data2[8].x;
+ y = data2[8].y;
+ data1.length = 0;
+ data2 = data1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(4, 5));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(array_push)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[] data;
+ function test() returns (uint x, uint y, uint z, uint l) {
+ data.push(5);
+ x = data[0];
+ data.push(4);
+ y = data[1];
+ l = data.push(3);
+ z = data[2];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(5, 4, 3, 3));
+}
+
+BOOST_AUTO_TEST_CASE(byte_array_push)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function test() returns (bool x) {
+ if (data.push(5) != 1) return true;
+ if (data[0] != 5) return true;
+ data.push(4);
+ if (data[1] != 4) return true;
+ uint l = data.push(3);
+ if (data[2] != 3) return true;
+ if (l != 3) return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(external_array_args)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function test(uint[8] a, uint[] b, uint[5] c, uint a_index, uint b_index, uint c_index)
+ external returns (uint av, uint bv, uint cv) {
+ av = a[a_index];
+ bv = b[b_index];
+ cv = c[c_index];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ bytes params = encodeArgs(
+ 1, 2, 3, 4, 5, 6, 7, 8, // a
+ 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b
+ 21, 22, 23, 24, 25, // c
+ 0, 1, 2, // (a,b,c)_index
+ 3, // b.length
+ 11, 12, 13 // b
+ );
+ BOOST_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params) == encodeArgs(1, 12, 23));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_index_access)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function direct(bytes arg, uint index) external returns (uint) {
+ return uint(arg[index]);
+ }
+ function storageCopyRead(bytes arg, uint index) external returns (uint) {
+ data = arg;
+ return uint(data[index]);
+ }
+ function storageWrite() external returns (uint) {
+ data.length = 35;
+ data[31] = 0x77;
+ data[32] = 0x14;
+
+ data[31] = 1;
+ data[31] |= 8;
+ data[30] = 1;
+ data[32] = 3;
+ return uint(data[30]) * 0x100 | uint(data[31]) * 0x10 | uint(data[32]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ string array{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33};
+ BOOST_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array) == encodeArgs(33));
+ BOOST_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array) == encodeArgs(33));
+ BOOST_CHECK(callContractFunction("storageWrite()") == encodeArgs(0x193));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_delete_element)
+{
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function test1() external returns (bool) {
+ data.length = 100;
+ for (uint i = 0; i < data.length; i++)
+ data[i] = byte(i);
+ delete data[94];
+ delete data[96];
+ delete data[98];
+ return data[94] == 0 && data[95] == 95 && data[96] == 0 && data[97] == 97;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test1()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[9] m_data;
+ uint[] m_data_dyn;
+ uint8[][] m_byte_data;
+ function store(uint[9] a, uint8[3][] b) external returns (uint8) {
+ m_data = a;
+ m_data_dyn = a;
+ m_byte_data = b;
+ return b[3][1]; // note that access and declaration are reversed to each other
+ }
+ function retrieve() returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) {
+ a = m_data.length;
+ b = m_data[7];
+ c = m_data_dyn.length;
+ d = m_data_dyn[7];
+ e = m_byte_data.length;
+ f = m_byte_data[3].length;
+ g = m_byte_data[3][1];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, // a
+ u256(32 * (9 + 1)),
+ 4, // size of b
+ 1, 2, 3, // b[0]
+ 11, 12, 13, // b[1]
+ 21, 22, 23, // b[2]
+ 31, 32, 33 // b[3]
+ )) == encodeArgs(32));
+ BOOST_CHECK(callContractFunction("retrieve()") == encodeArgs(
+ 9, 28, 9, 28,
+ 4, 3, 32));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_nested_array)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint[4][] a;
+ uint[10][] b;
+ uint[][] c;
+ function test(uint[2][] d) external returns (uint) {
+ a = d;
+ b = a;
+ c = b;
+ return c[1][1] | c[1][2] | c[1][3] | c[1][4];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test(uint256[2][])", encodeArgs(
+ 32, 3,
+ 7, 8,
+ 9, 10,
+ 11, 12
+ )) == encodeArgs(10));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
+{
+ char const* sourceCode = R"(
+ contract c {
+ mapping(uint=>uint)[90][] large;
+ mapping(uint=>uint)[3][] small;
+ function test() returns (uint r) {
+ large.length = small.length = 7;
+ large[3][2][0] = 2;
+ large[1] = large[3];
+ small[3][2][0] = 2;
+ small[1] = small[2];
+ r = ((
+ small[3][2][0] * 0x100 |
+ small[1][2][0]) * 0x100 |
+ large[3][2][0]) * 0x100 |
+ large[1][2][0];
+ delete small;
+ delete large;
+ }
+ function clear() returns (uint r) {
+ large.length = small.length = 7;
+ small[3][2][0] = 0;
+ large[3][2][0] = 0;
+ small.length = large.length = 0;
+ return 7;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(0x02000200));
+ // storage is not empty because we cannot delete the mappings
+ BOOST_CHECK(!m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("clear()") == encodeArgs(7));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ function Base(uint i)
+ {
+ m_i = i;
+ }
+ uint public m_i;
+ }
+ contract Derived is Base(2) {
+ function Derived(uint i) Base(i)
+ {}
+ }
+ contract Final is Derived(4) {
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4));
+}
+
+BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ function Base(uint j)
+ {
+ m_i = j;
+ }
+ uint public m_i;
+ }
+ contract Base1 is Base(3) {
+ function Base1(uint k) Base(k*k) {}
+ }
+ contract Derived is Base(3), Base1(2) {
+ function Derived(uint i) Base(i) Base1(i)
+ {}
+ }
+ contract Final is Derived(4) {
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4));
+}
+
+BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ function Base(uint i)
+ {
+ m_i = i;
+ }
+ uint public m_i;
+ }
+ contract Base1 is Base(3) {}
+ contract Derived is Base(2), Base1 {
+ function Derived(uint i) Base(i) {}
+ }
+ contract Final is Derived(4) {
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("m_i()") == encodeArgs(4));
+}
+
+BOOST_AUTO_TEST_CASE(simple_constant_variables_test)
+{
+ char const* sourceCode = R"(
+ contract Foo {
+ function getX() returns (uint r) { return x; }
+ uint constant x = 56;
+ })";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("getX()") == encodeArgs(56));
+}
+
+BOOST_AUTO_TEST_CASE(constant_variables)
+{
+ //for now constant specifier is valid only for uint, bytesXX, string and enums
+ char const* sourceCode = R"(
+ contract Foo {
+ uint constant x = 56;
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ ActionChoices constant choices = ActionChoices.GoLeft;
+ bytes32 constant st = "abc\x00\xff__";
+ })";
+ compileAndRun(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(packed_storage_structs_uint)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct str { uint8 a; uint16 b; uint248 c; }
+ str data;
+ function test() returns (uint) {
+ data.a = 2;
+ if (data.a != 2) return 2;
+ data.b = 0xabcd;
+ if (data.b != 0xabcd) return 3;
+ data.c = 0x1234567890;
+ if (data.c != 0x1234567890) return 4;
+ if (data.a != 2) return 5;
+ data.a = 8;
+ if (data.a != 8) return 6;
+ if (data.b != 0xabcd) return 7;
+ data.b = 0xdcab;
+ if (data.b != 0xdcab) return 8;
+ if (data.c != 0x1234567890) return 9;
+ data.c = 0x9876543210;
+ if (data.c != 0x9876543210) return 10;
+ return 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(packed_storage_structs_enum)
+{
+ char const* sourceCode = R"(
+ contract C {
+ enum small { A, B, C, D }
+ enum larger { A, B, C, D, E}
+ struct str { small a; small b; larger c; larger d; }
+ str data;
+ function test() returns (uint) {
+ data.a = small.B;
+ if (data.a != small.B) return 2;
+ data.b = small.C;
+ if (data.b != small.C) return 3;
+ data.c = larger.D;
+ if (data.c != larger.D) return 4;
+ if (data.a != small.B) return 5;
+ data.a = small.C;
+ if (data.a != small.C) return 6;
+ if (data.b != small.C) return 7;
+ data.b = small.D;
+ if (data.b != small.D) return 8;
+ if (data.c != larger.D) return 9;
+ data.c = larger.B;
+ if (data.c != larger.B) return 10;
+ return 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(1));
+}
+
+BOOST_AUTO_TEST_CASE(packed_storage_structs_bytes)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct s1 { byte a; byte b; bytes10 c; bytes9 d; bytes10 e; }
+ struct s2 { byte a; s1 inner; byte b; byte c; }
+ byte x;
+ s2 data;
+ byte y;
+ function test() returns (bool) {
+ x = 1;
+ data.a = 2;
+ data.inner.a = 3;
+ data.inner.b = 4;
+ data.inner.c = "1234567890";
+ data.inner.d = "123456789";
+ data.inner.e = "abcdefghij";
+ data.b = 5;
+ data.c = 6;
+ y = 7;
+ return x == 1 && data.a == 2 && data.inner.a == 3 && data.inner.b == 4 &&
+ data.inner.c == "1234567890" && data.inner.d == "123456789" &&
+ data.inner.e == "abcdefghij" && data.b == 5 && data.c == 6 && y == 7;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(packed_storage_structs_delete)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct str { uint8 a; uint16 b; uint8 c; }
+ uint8 x;
+ uint16 y;
+ str data;
+ function test() returns (uint) {
+ x = 1;
+ y = 2;
+ data.a = 2;
+ data.b = 0xabcd;
+ data.c = 0xfa;
+ if (x != 1 || y != 2 || data.a != 2 || data.b != 0xabcd || data.c != 0xfa)
+ return 2;
+ delete y;
+ delete data.b;
+ if (x != 1 || y != 0 || data.a != 2 || data.b != 0 || data.c != 0xfa)
+ return 3;
+ delete x;
+ delete data;
+ return 1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(1));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint k) returns(uint d) { return k; }
+ function f(uint a, uint b) returns(uint d) { return a + b; }
+ function g() returns(uint d) { return f(3); }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(3));
+}
+
+BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a, uint b) returns(uint d) { return a + b; }
+ function f(uint k) returns(uint d) { return k; }
+ function g() returns(uint d) { return f(3, 7); }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(10));
+}
+
+BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a, uint b) returns(uint d) { return a + b; }
+ function f(uint k) returns(uint d) { return k; }
+ function g(bool flag) returns(uint d) {
+ if (flag)
+ return f(3);
+ else
+ return f(3, 7);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3));
+ BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10));
+}
+
+BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct)
+{
+ char const* sourceCode = R"(
+ contract B { function f() returns(uint) { return 10; } }
+ contract C is B {
+ function f(uint i) returns(uint) { return 2 * i; }
+ function g() returns(uint) { return f(1); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect)
+{
+ char const* sourceCode = R"(
+ contract A { function f(uint a) returns(uint) { return 2 * a; } }
+ contract B { function f() returns(uint) { return 10; } }
+ contract C is A, B {
+ function g() returns(uint) { return f(); }
+ function h() returns(uint) { return f(1); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(10));
+ BOOST_CHECK(callContractFunction("h()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(super_overload)
+{
+ char const* sourceCode = R"(
+ contract A { function f(uint a) returns(uint) { return 2 * a; } }
+ contract B { function f(bool b) returns(uint) { return 10; } }
+ contract C is A, B {
+ function g() returns(uint) { return super.f(true); }
+ function h() returns(uint) { return super.f(1); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(10));
+ BOOST_CHECK(callContractFunction("h()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(packed_storage_signed)
+{
+ char const* sourceCode = R"(
+ contract C {
+ int8 a;
+ uint8 b;
+ int8 c;
+ uint8 d;
+ function test() returns (uint x1, uint x2, uint x3, uint x4) {
+ a = -2;
+ b = -uint8(a) * 2;
+ c = a * int8(120) * int8(121);
+ x1 = uint(a);
+ x2 = b;
+ x3 = uint(c);
+ x4 = d;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK( callContractFunction("test()") == encodeArgs(u256(-2), u256(4), u256(-112), u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(external_types_in_calls)
+{
+ char const* sourceCode = R"(
+ contract C1 { C1 public bla; function C1(C1 x) { bla = x; } }
+ contract C {
+ function test() returns (C1 x, C1 y) {
+ C1 c = new C1(C1(9));
+ x = c.bla();
+ y = this.t1(C1(7));
+ }
+ function t1(C1 a) returns (C1) { return a; }
+ function() returns (C1) { return C1(9); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(9), u256(7)));
+ BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9)));
+}
+
+BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
+{
+ // bug #1798
+ char const* sourceCode = R"(
+ contract init {
+ function isOk() returns (bool) { return false; }
+ bool public ok = false;
+ }
+ contract fix {
+ function isOk() returns (bool) { return true; }
+ bool public ok = true;
+ }
+
+ contract init_fix is init, fix {
+ function checkOk() returns (bool) { return ok; }
+ }
+ contract fix_init is fix, init {
+ function checkOk() returns (bool) { return ok; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "init_fix");
+ BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(true));
+ BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true));
+
+ compileAndRun(sourceCode, 0, "fix_init");
+ BOOST_CHECK(callContractFunction("isOk()") == encodeArgs(false));
+ BOOST_CHECK(callContractFunction("ok()") == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(proper_overwriting_accessor_by_function)
+{
+ // bug #1798
+ char const* sourceCode = R"(
+ contract attribute {
+ bool ok = false;
+ }
+ contract func {
+ function ok() returns (bool) { return true; }
+ }
+
+ contract attr_func is attribute, func {
+ function checkOk() returns (bool) { return ok(); }
+ }
+ contract func_attr is func, attribute {
+ function checkOk() returns (bool) { return ok; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "attr_func");
+ BOOST_CHECK(callContractFunction("ok()") == encodeArgs(true));
+ compileAndRun(sourceCode, 0, "func_attr");
+ BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(false));
+}
+
+
+BOOST_AUTO_TEST_CASE(overwriting_inheritance)
+{
+ // bug #1798
+ char const* sourceCode = R"(
+ contract A {
+ function ok() returns (uint) { return 1; }
+ }
+ contract B {
+ function ok() returns (uint) { return 2; }
+ }
+ contract C {
+ uint ok = 6;
+ }
+ contract AB is A, B {
+ function ok() returns (uint) { return 4; }
+ }
+ contract reversedE is C, AB {
+ function checkOk() returns (uint) { return ok(); }
+ }
+ contract E is AB, C {
+ function checkOk() returns (uint) { return ok; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "reversedE");
+ BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(4));
+ compileAndRun(sourceCode, 0, "E");
+ BOOST_CHECK(callContractFunction("checkOk()") == encodeArgs(6));
+}
+
+BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct testStruct
+ {
+ uint m_value;
+ }
+ testStruct data1;
+ testStruct data2;
+ testStruct data3;
+ function test()
+ {
+ data1.m_value = 2;
+ }
+ function assign() returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1)
+ {
+ testStruct x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2
+ data2 = data1; // should copy data. data2.m_value == 2
+
+ ret_local = x.m_value; // = 2
+ ret_global = data2.m_value; // = 2
+
+ x.m_value = 3;
+ data3 = x; //should copy the data. data3.m_value == 3
+ ret_global3 = data3.m_value; // = 3
+ ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "test");
+ BOOST_CHECK(callContractFunction("assign()") == encodeArgs(2, 2, 3, 3));
+}
+
+BOOST_AUTO_TEST_CASE(struct_delete_member)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct testStruct
+ {
+ uint m_value;
+ }
+ testStruct data1;
+ function test()
+ {
+ data1.m_value = 2;
+ }
+ function deleteMember() returns (uint ret_value)
+ {
+ testStruct x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0
+ x.m_value = 4;
+ delete x.m_value;
+ ret_value = data1.m_value;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "test");
+ BOOST_CHECK(callContractFunction("deleteMember()") == encodeArgs(0));
+}
+
+BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct testStruct
+ {
+ uint m_value;
+ }
+ mapping (uint => testStruct) campaigns;
+
+ function test()
+ {
+ campaigns[0].m_value = 2;
+ }
+ function deleteIt() returns (uint)
+ {
+ delete campaigns[0];
+ return campaigns[0].m_value;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "test");
+ BOOST_CHECK(callContractFunction("deleteIt()") == encodeArgs(0));
+}
+
+BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint[3] arr;
+ bool public test = false;
+ function getElement(uint i) returns (uint)
+ {
+ return arr[i];
+ }
+ function testIt() returns (bool)
+ {
+ uint i = this.getElement(5);
+ test = true;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "A");
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
+ BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(false));
+}
+
+BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail)
+{
+ char const* sourceCode = R"(
+ contract A {
+ function A()
+ {
+ this.call("123");
+ }
+ }
+ contract B {
+ uint public test = 1;
+ function testIt()
+ {
+ A a = new A();
+ ++test;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "B");
+
+ BOOST_CHECK(callContractFunction("testIt()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund)
+{
+ char const* sourceCode = R"(
+ contract A {
+ uint public test = 1;
+ uint[3] arr;
+ function A()
+ {
+ uint index = 5;
+ test = arr[index];
+ ++test;
+ }
+ }
+ )";
+ BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A").empty());
+}
+
+BOOST_AUTO_TEST_CASE(positive_integers_to_signed)
+{
+ char const* sourceCode = R"(
+ contract test {
+ int8 public x = 2;
+ int8 public y = 127;
+ int16 public q = 250;
+ }
+ )";
+ compileAndRun(sourceCode, 0, "test");
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(2));
+ BOOST_CHECK(callContractFunction("y()") == encodeArgs(127));
+ BOOST_CHECK(callContractFunction("q()") == encodeArgs(250));
+}
+
+BOOST_AUTO_TEST_CASE(failing_send)
+{
+ char const* sourceCode = R"(
+ contract Helper {
+ uint[] data;
+ function () {
+ data[9]; // trigger exception
+ }
+ }
+ contract Main {
+ function callHelper(address _a) returns (bool r, uint bal) {
+ r = !_a.send(5);
+ bal = this.balance;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Helper");
+ u160 const c_helperAddress = m_contractAddress;
+ compileAndRun(sourceCode, 20, "Main");
+ BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20));
+}
+
+BOOST_AUTO_TEST_CASE(reusing_memory)
+{
+ // Invoke some features that use memory and test that they do not interfere with each other.
+ char const* sourceCode = R"(
+ contract Helper {
+ uint public flag;
+ function Helper(uint x) {
+ flag = x;
+ }
+ }
+ contract Main {
+ mapping(uint => uint) map;
+ function f(uint x) returns (uint) {
+ map[x] = x;
+ return (new Helper(uint(sha3(this.g(map[x]))))).flag();
+ }
+ function g(uint a) returns (uint)
+ {
+ return map[a];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34)))));
+}
+
+BOOST_AUTO_TEST_CASE(return_string)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ string public s;
+ function set(string _s) external {
+ s = _s;
+ }
+ function get1() returns (string r) {
+ return s;
+ }
+ function get2() returns (string r) {
+ r = s;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s("Julia");
+ bytes args = encodeArgs(u256(0x20), u256(s.length()), s);
+ BOOST_REQUIRE(callContractFunction("set(string)", asString(args)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get1()") == args);
+ BOOST_CHECK(callContractFunction("get2()") == args);
+ BOOST_CHECK(callContractFunction("s()") == args);
+}
+
+BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ string public s1;
+ string public s2;
+ function set(string _s1, uint x, string _s2) external returns (uint) {
+ s1 = _s1;
+ s2 = _s2;
+ return x;
+ }
+ function get() returns (string r1, string r2) {
+ r1 = s1;
+ r2 = s2;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s1(
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+ );
+ string s2(
+ "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
+ "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
+ "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
+ "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
+ "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"
+ );
+ vector<size_t> lengthes{0, 30, 32, 63, 64, 65, 210, 300};
+ for (auto l1: lengthes)
+ for (auto l2: lengthes)
+ {
+ bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1));
+ bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2));
+ bytes args = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2;
+ BOOST_REQUIRE(
+ callContractFunction("set(string,uint256,string)", asString(args)) ==
+ encodeArgs(u256(l1))
+ );
+ bytes result = encodeArgs(u256(0x40), u256(0x40 + dyn1.size())) + dyn1 + dyn2;
+ BOOST_CHECK(callContractFunction("get()") == result);
+ BOOST_CHECK(callContractFunction("s1()") == encodeArgs(0x20) + dyn1);
+ BOOST_CHECK(callContractFunction("s2()") == encodeArgs(0x20) + dyn2);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(accessor_involving_strings)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ struct stringData { string a; uint b; string c; }
+ mapping(uint => stringData[]) public data;
+ function set(uint x, uint y, string a, uint b, string c) external returns (bool) {
+ data[x].length = y + 1;
+ data[x][y].a = a;
+ data[x][y].b = b;
+ data[x][y].c = c;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ");
+ bytes s1Data = encodeArgs(u256(s1.length()), s1);
+ bytes s2Data = encodeArgs(u256(s2.length()), s2);
+ u256 b = 765;
+ u256 x = 7;
+ u256 y = 123;
+ bytes args = encodeArgs(x, y, u256(0xa0), b, u256(0xa0 + s1Data.size()), s1Data, s2Data);
+ bytes result = encodeArgs(u256(0x60), b, u256(0x60 + s1Data.size()), s1Data, s2Data);
+ BOOST_REQUIRE(callContractFunction("set(uint256,uint256,string,uint256,string)", asString(args)) == encodeArgs(true));
+ BOOST_REQUIRE(callContractFunction("data(uint256,uint256)", x, y) == result);
+}
+
+BOOST_AUTO_TEST_CASE(bytes_in_function_calls)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ string public s1;
+ string public s2;
+ function set(string _s1, uint x, string _s2) returns (uint) {
+ s1 = _s1;
+ s2 = _s2;
+ return x;
+ }
+ function setIndirectFromMemory(string _s1, uint x, string _s2) returns (uint) {
+ return this.set(_s1, x, _s2);
+ }
+ function setIndirectFromCalldata(string _s1, uint x, string _s2) external returns (uint) {
+ return this.set(_s1, x, _s2);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ");
+ vector<size_t> lengthes{0, 31, 64, 65};
+ for (auto l1: lengthes)
+ for (auto l2: lengthes)
+ {
+ bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1));
+ bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2));
+ bytes args1 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn1.size())) + dyn1 + dyn2;
+ BOOST_REQUIRE(
+ callContractFunction("setIndirectFromMemory(string,uint256,string)", asString(args1)) ==
+ encodeArgs(u256(l1))
+ );
+ BOOST_CHECK(callContractFunction("s1()") == encodeArgs(0x20) + dyn1);
+ BOOST_CHECK(callContractFunction("s2()") == encodeArgs(0x20) + dyn2);
+ // swapped
+ bytes args2 = encodeArgs(u256(0x60), u256(l1), u256(0x60 + dyn2.size())) + dyn2 + dyn1;
+ BOOST_REQUIRE(
+ callContractFunction("setIndirectFromCalldata(string,uint256,string)", asString(args2)) ==
+ encodeArgs(u256(l1))
+ );
+ BOOST_CHECK(callContractFunction("s1()") == encodeArgs(0x20) + dyn2);
+ BOOST_CHECK(callContractFunction("s2()") == encodeArgs(0x20) + dyn1);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(return_bytes_internal)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ bytes s1;
+ function doSet(bytes _s1) returns (bytes _r1) {
+ s1 = _s1;
+ _r1 = s1;
+ }
+ function set(bytes _s1) external returns (uint _r, bytes _r1) {
+ _r1 = doSet(_s1);
+ _r = _r1.length;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ vector<size_t> lengthes{0, 31, 64, 65};
+ for (auto l1: lengthes)
+ {
+ bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1));
+ bytes args1 = encodeArgs(u256(0x20)) + dyn1;
+ BOOST_REQUIRE(
+ callContractFunction("set(bytes)", asString(args1)) ==
+ encodeArgs(u256(l1), u256(0x40)) + dyn1
+ );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(bytes_index_access_memory)
+{
+ char const* sourceCode = R"(
+ contract Main {
+ function f(bytes _s1, uint i1, uint i2, uint i3) returns (byte c1, byte c2, byte c3) {
+ c1 = _s1[i1];
+ c2 = intern(_s1, i2);
+ c3 = internIndirect(_s1)[i3];
+ }
+ function intern(bytes _s1, uint i) returns (byte c) {
+ return _s1[i];
+ }
+ function internIndirect(bytes _s1) returns (bytes) {
+ return _s1;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Main");
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ bytes dyn1 = encodeArgs(u256(s1.length()), s1);
+ bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1;
+ BOOST_REQUIRE(
+ callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) ==
+ encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]})
+ );
+}
+
+BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ uint public m_x;
+ bytes public m_s;
+ function Test(uint x, bytes s) {
+ m_x = x;
+ m_s = s;
+ }
+ }
+ )";
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ bytes dyn1 = encodeArgs(u256(s1.length()), s1);
+ u256 x = 7;
+ bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
+ compileAndRun(sourceCode, 0, "Test", args1);
+ BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x));
+ BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1);
+}
+
+BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ uint public m_x;
+ bytes m_s;
+ function Base(uint x, bytes s) {
+ m_x = x;
+ m_s = s;
+ }
+ function part(uint i) returns (byte) {
+ return m_s[i];
+ }
+ }
+ contract Main is Base {
+ function Main(bytes s, uint x) Base(x, f(s)) {}
+ function f(bytes s) returns (bytes) {
+ return s;
+ }
+ }
+ contract Creator {
+ function f(uint x, bytes s) returns (uint r, byte ch) {
+ var c = new Main(s, x);
+ r = c.m_x();
+ ch = c.part(x);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Creator");
+ string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz");
+ bytes dyn1 = encodeArgs(u256(s1.length()), s1);
+ u256 x = 7;
+ bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
+ BOOST_REQUIRE(
+ callContractFunction("f(uint256,bytes)", asString(args1)) ==
+ encodeArgs(x, string{s1[unsigned(x)]})
+ );
+}
+
+BOOST_AUTO_TEST_CASE(arrays_in_constructors)
+{
+ char const* sourceCode = R"(
+ contract Base {
+ uint public m_x;
+ address[] m_s;
+ function Base(uint x, address[] s) {
+ m_x = x;
+ m_s = s;
+ }
+ function part(uint i) returns (address) {
+ return m_s[i];
+ }
+ }
+ contract Main is Base {
+ function Main(address[] s, uint x) Base(x, f(s)) {}
+ function f(address[] s) returns (address[]) {
+ return s;
+ }
+ }
+ contract Creator {
+ function f(uint x, address[] s) returns (uint r, address ch) {
+ var c = new Main(s, x);
+ r = c.m_x();
+ ch = c.part(x);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Creator");
+ vector<u256> s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ bytes dyn1 = encodeArgs(u256(s1.size()), s1);
+ u256 x = 7;
+ bytes args1 = encodeArgs(x, u256(0x40)) + dyn1;
+ BOOST_REQUIRE(
+ callContractFunction("f(uint256,address[])", asString(args1)) ==
+ encodeArgs(x, s1[unsigned(x)])
+ );
+}
+
+BOOST_AUTO_TEST_CASE(fixed_arrays_in_constructors)
+{
+ char const* sourceCode = R"(
+ contract Creator {
+ uint public r;
+ address public ch;
+ function Creator(address[3] s, uint x) {
+ r = x;
+ ch = s[2];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Creator", encodeArgs(u256(1), u256(2), u256(3), u256(4)));
+ BOOST_REQUIRE(callContractFunction("r()") == encodeArgs(u256(4)));
+ BOOST_REQUIRE(callContractFunction("ch()") == encodeArgs(u256(3)));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ uint24[] public data;
+ function set(uint24[] _data) returns (uint) {
+ data = _data;
+ return data.length;
+ }
+ function get() returns (uint24[]) {
+ return data;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+ BOOST_REQUIRE(
+ callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) ==
+ encodeArgs(u256(data.size()))
+ );
+ BOOST_CHECK(callContractFunction("data(uint256)", u256(7)) == encodeArgs(u256(8)));
+ BOOST_CHECK(callContractFunction("data(uint256)", u256(15)) == encodeArgs(u256(16)));
+ BOOST_CHECK(callContractFunction("data(uint256)", u256(18)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0x20), u256(data.size()), data));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ uint24[3][] public data;
+ function set(uint24[3][] _data) returns (uint) {
+ data = _data;
+ return data.length;
+ }
+ function get() returns (uint24[3][]) {
+ return data;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+ BOOST_REQUIRE(
+ callContractFunction("set(uint24[3][])", u256(0x20), u256(data.size() / 3), data) ==
+ encodeArgs(u256(data.size() / 3))
+ );
+ BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(2), u256(2)) == encodeArgs(u256(9)));
+ BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(5), u256(1)) == encodeArgs(u256(17)));
+ BOOST_CHECK(callContractFunction("data(uint256,uint256)", u256(6), u256(0)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("get()") == encodeArgs(u256(0x20), u256(data.size() / 3), data));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function set(uint24[3][] _data, uint a, uint b) returns (uint l, uint e) {
+ l = _data.length;
+ e = _data[a][b];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+ BOOST_REQUIRE(callContractFunction(
+ "set(uint24[3][],uint256,uint256)",
+ u256(0x60),
+ u256(3),
+ u256(2),
+ u256(data.size() / 3),
+ data
+ ) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
+}
+
+BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function set(bytes _data, uint i) returns (uint l, byte c) {
+ l = _data.length;
+ c = _data[i];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ string data("abcdefgh");
+ BOOST_REQUIRE(callContractFunction(
+ "set(bytes,uint256)",
+ u256(0x40),
+ u256(3),
+ u256(data.size()),
+ data
+ ) == encodeArgs(u256(data.size()), string("d")));
+}
+
+BOOST_AUTO_TEST_CASE(storage_array_ref)
+{
+ char const* sourceCode = R"(
+ contract BinarySearch {
+ /// Finds the position of _value in the sorted list _data.
+ /// Note that "internal" is important here, because storage references only work for internal or private functions
+ function find(uint[] storage _data, uint _value) internal returns (uint o_position) {
+ return find(_data, 0, _data.length, _value);
+ }
+ function find(uint[] storage _data, uint _begin, uint _len, uint _value) private returns (uint o_position) {
+ if (_len == 0 || (_len == 1 && _data[_begin] != _value))
+ return uint(-1); // failure
+ uint halfLen = _len / 2;
+ uint v = _data[_begin + halfLen];
+ if (_value < v)
+ return find(_data, _begin, halfLen, _value);
+ else if (_value > v)
+ return find(_data, _begin + halfLen + 1, halfLen - 1, _value);
+ else
+ return _begin + halfLen;
+ }
+ }
+
+ contract Store is BinarySearch {
+ uint[] data;
+ function add(uint v) {
+ data.length++;
+ data[data.length - 1] = v;
+ }
+ function find(uint v) returns (uint) {
+ return find(data, v);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Store");
+ BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
+ BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
+ BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(11)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(17)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(27)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(31)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(32)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(66)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("add(uint256)", u256(177)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(27)) == encodeArgs(u256(3)));
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(32)) == encodeArgs(u256(5)));
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(176)) == encodeArgs(u256(-1)));
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(0)) == encodeArgs(u256(-1)));
+ BOOST_CHECK(callContractFunction("find(uint256)", u256(400)) == encodeArgs(u256(-1)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_types_initialisation)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ mapping(uint=>uint) data;
+ function stat() returns (uint[5])
+ {
+ data[2] = 3; // make sure to use some memory
+ }
+ function dyn() returns (uint[]) { stat(); }
+ function nested() returns (uint[3][]) { stat(); }
+ function nestedStat() returns (uint[3][7]) { stat(); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ BOOST_CHECK(callContractFunction("stat()") == encodeArgs(vector<u256>(5)));
+ BOOST_CHECK(callContractFunction("dyn()") == encodeArgs(u256(0x20), u256(0)));
+ BOOST_CHECK(callContractFunction("nested()") == encodeArgs(u256(0x20), u256(0)));
+ BOOST_CHECK(callContractFunction("nestedStat()") == encodeArgs(vector<u256>(3 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_arrays_delete)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function del() returns (uint24[3][4]) {
+ uint24[3][4] memory x;
+ for (uint24 i = 0; i < x.length; i ++)
+ for (uint24 j = 0; j < x[i].length; j ++)
+ x[i][j] = i * 0x10 + j;
+ delete x[1];
+ delete x[3][2];
+ return x;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data(3 * 4);
+ for (unsigned i = 0; i < 4; i++)
+ for (unsigned j = 0; j < 3; j++)
+ {
+ u256 v = 0;
+ if (!(i == 1 || (i == 3 && j == 2)))
+ v = i * 0x10 + j;
+ data[i * 3 + j] = v;
+ }
+ BOOST_CHECK(callContractFunction("del()") == encodeArgs(data));
+}
+
+BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function set(uint24[3][4] x) {
+ x[2][2] = 1;
+ x[3][2] = 7;
+ }
+ function f() returns (uint24[3][4]){
+ uint24[3][4] memory data;
+ set(data);
+ return data;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data(3 * 4);
+ data[3 * 2 + 2] = 1;
+ data[3 * 3 + 2] = 7;
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(data));
+}
+
+BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ uint24[3][][4] data;
+ function set(uint24[3][][4] x) internal returns (uint24[3][][4]) {
+ x[1][2][2] = 1;
+ x[1][3][2] = 7;
+ return x;
+ }
+ function f() returns (uint24[3][]) {
+ data[1].length = 4;
+ return set(data)[1];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ vector<u256> data(3 * 4);
+ data[3 * 2 + 2] = 1;
+ data[3 * 3 + 2] = 7;
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x20), u256(4), data));
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_read_write)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint8 x; uint16 y; uint z; uint8[2] a; }
+ S[5] data;
+ function testInit() returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) {
+ S[2] memory d;
+ x = d[0].x;
+ y = d[0].y;
+ z = d[0].z;
+ a = d[0].a[1];
+ flag = true;
+ }
+ function testCopyRead() returns (uint8 x, uint16 y, uint z, uint8 a) {
+ data[2].x = 1;
+ data[2].y = 2;
+ data[2].z = 3;
+ data[2].a[1] = 4;
+ S memory s = data[2];
+ x = s.x;
+ y = s.y;
+ z = s.z;
+ a = s.a[1];
+ }
+ function testAssign() returns (uint8 x, uint16 y, uint z, uint8 a) {
+ S memory s;
+ s.x = 1;
+ s.y = 2;
+ s.z = 3;
+ s.a[1] = 4;
+ x = s.x;
+ y = s.y;
+ z = s.z;
+ a = s.a[1];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ BOOST_CHECK(callContractFunction("testInit()") == encodeArgs(u256(0), u256(0), u256(0), u256(0), true));
+ BOOST_CHECK(callContractFunction("testCopyRead()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
+ BOOST_CHECK(callContractFunction("testAssign()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_as_function_args)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint8 x; uint16 y; uint z; }
+ function test() returns (uint x, uint y, uint z) {
+ S memory data = combine(1, 2, 3);
+ x = extract(data, 0);
+ y = extract(data, 1);
+ z = extract(data, 2);
+ }
+ function extract(S s, uint which) internal returns (uint x) {
+ if (which == 0) return s.x;
+ else if (which == 1) return s.y;
+ else return s.z;
+ }
+ function combine(uint8 x, uint16 y, uint z) internal returns (S s) {
+ s.x = x;
+ s.y = y;
+ s.z = z;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_nested)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint8 x; uint16 y; uint z; }
+ struct X { uint8 x; S s; }
+ function test() returns (uint a, uint x, uint y, uint z) {
+ X memory d = combine(1, 2, 3, 4);
+ a = extract(d, 0);
+ x = extract(d, 1);
+ y = extract(d, 2);
+ z = extract(d, 3);
+ }
+ function extract(X s, uint which) internal returns (uint x) {
+ if (which == 0) return s.x;
+ else if (which == 1) return s.s.x;
+ else if (which == 2) return s.s.y;
+ else return s.s.z;
+ }
+ function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X s) {
+ s.x = a;
+ s.s.x = x;
+ s.s.y = y;
+ s.s.z = z;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3), u256(4)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_nested_load)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint8 x; uint16 y; uint z; }
+ struct X { uint8 x; S s; uint8[2] a; }
+ X m_x;
+ function load() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) {
+ m_x.x = 1;
+ m_x.s.x = 2;
+ m_x.s.y = 3;
+ m_x.s.z = 4;
+ m_x.a[0] = 5;
+ m_x.a[1] = 6;
+ X memory d = m_x;
+ a = d.x;
+ x = d.s.x;
+ y = d.s.y;
+ z = d.s.z;
+ a1 = d.a[0];
+ a2 = d.a[1];
+ }
+ function store() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) {
+ X memory d;
+ d.x = 1;
+ d.s.x = 2;
+ d.s.y = 3;
+ d.s.z = 4;
+ d.a[0] = 5;
+ d.a[1] = 6;
+ m_x = d;
+ a = m_x.x;
+ x = m_x.s.x;
+ y = m_x.s.y;
+ z = m_x.s.z;
+ a1 = m_x.a[0];
+ a2 = m_x.a[1];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+
+ auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6));
+ BOOST_CHECK(callContractFunction("load()") == out);
+ BOOST_CHECK(callContractFunction("store()") == out);
+}
+
+BOOST_AUTO_TEST_CASE(struct_constructor_nested)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct X { uint x1; uint x2; }
+ struct S { uint s1; uint[3] s2; X s3; }
+ S s;
+ function C() {
+ uint[3] memory s2;
+ s2[1] = 9;
+ s = S(1, s2, X(4, 5));
+ }
+ function get() returns (uint s1, uint[3] s2, uint x1, uint x2)
+ {
+ s1 = s.s1;
+ s2 = s.s2;
+ x1 = s.s3.x1;
+ x2 = s.s3.x2;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+
+ auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5));
+ BOOST_CHECK(callContractFunction("get()") == out);
+}
+
+BOOST_AUTO_TEST_CASE(struct_named_constructor)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct S { uint a; bool x; }
+ S public s;
+ function C() {
+ s = S({a: 1, x: true});
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+
+ BOOST_CHECK(callContractFunction("s()") == encodeArgs(u256(1), true));
+}
+
+BOOST_AUTO_TEST_CASE(literal_strings)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ string public long;
+ string public medium;
+ string public short;
+ string public empty;
+ function f() returns (string) {
+ long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ short = "123";
+ empty = "";
+ return "Hello, World!";
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ string medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ string shortStr = "123";
+ string hello = "Hello, World!";
+
+ BOOST_CHECK(callContractFunction("f()") == encodeDyn(hello));
+ BOOST_CHECK(callContractFunction("long()") == encodeDyn(longStr));
+ BOOST_CHECK(callContractFunction("medium()") == encodeDyn(medium));
+ BOOST_CHECK(callContractFunction("short()") == encodeDyn(shortStr));
+ BOOST_CHECK(callContractFunction("empty()") == encodeDyn(string()));
+}
+
+BOOST_AUTO_TEST_CASE(initialise_string_constant)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ string public short = "abcdef";
+ string public long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ string shortStr = "abcdef";
+
+ BOOST_CHECK(callContractFunction("long()") == encodeDyn(longStr));
+ BOOST_CHECK(callContractFunction("short()") == encodeDyn(shortStr));
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
+ S s;
+ function f() returns (uint) {
+ S memory x;
+ if (x.a != 0 || x.c != 0) return 1;
+ x.a = 4; x.c = 5;
+ s = x;
+ if (s.a != 4 || s.c != 5) return 2;
+ x = S(2, 3);
+ if (x.a != 2 || x.c != 3) return 3;
+ x = s;
+ if (s.a != 4 || s.c != 5) return 4;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(string_bytes_conversion)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ string s;
+ bytes b;
+ function f(string _s, uint n) returns (byte) {
+ b = bytes(_s);
+ s = string(b);
+ return bytes(s)[n];
+ }
+ function l() returns (uint) { return bytes(s).length; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ BOOST_CHECK(callContractFunction(
+ "f(string,uint256)",
+ u256(0x40),
+ u256(2),
+ u256(6),
+ string("abcdef")
+ ) == encodeArgs("c"));
+ BOOST_CHECK(callContractFunction("l()") == encodeArgs(u256(6)));
+}
+
+BOOST_AUTO_TEST_CASE(string_as_mapping_key)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ mapping(string => uint) data;
+ function set(string _s, uint _v) { data[_s] = _v; }
+ function get(string _s) returns (uint) { return data[_s]; }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ vector<string> strings{
+ "Hello, World!",
+ "Hello, World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1111",
+ "",
+ "1"
+ };
+ for (unsigned i = 0; i < strings.size(); i++)
+ BOOST_CHECK(callContractFunction(
+ "set(string,uint256)",
+ u256(0x40),
+ u256(7 + i),
+ u256(strings[i].size()),
+ strings[i]
+ ) == encodeArgs());
+ for (unsigned i = 0; i < strings.size(); i++)
+ BOOST_CHECK(callContractFunction(
+ "get(string)",
+ u256(0x20),
+ u256(strings[i].size()),
+ strings[i]
+ ) == encodeArgs(u256(7 + i)));
+}
+
+BOOST_AUTO_TEST_CASE(accessor_for_state_variable)
+{
+ char const* sourceCode = R"(
+ contract Lotto{
+ uint public ticketPrice = 500;
+ })";
+
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("ticketPrice()") == encodeArgs(u256(500)));
+}
+
+BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable)
+{
+ char const* sourceCode = R"(
+ contract Lotto{
+ uint constant public ticketPrice = 555;
+ })";
+
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("ticketPrice()") == encodeArgs(u256(555)));
+}
+
+BOOST_AUTO_TEST_CASE(constant_string_literal)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ bytes32 constant public b = "abcdefghijklmnopq";
+ string constant public x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca";
+
+ function Test() {
+ var xx = x;
+ var bb = b;
+ }
+ function getB() returns (bytes32) { return b; }
+ function getX() returns (string) { return x; }
+ function getX2() returns (string r) { r = x; }
+ function unused() returns (uint) {
+ "unusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunused";
+ return 2;
+ }
+ }
+ )";
+
+ compileAndRun(sourceCode);
+ string longStr = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca";
+ string shortStr = "abcdefghijklmnopq";
+ BOOST_CHECK(callContractFunction("b()") == encodeArgs(shortStr));
+ BOOST_CHECK(callContractFunction("x()") == encodeDyn(longStr));
+ BOOST_CHECK(callContractFunction("getB()") == encodeArgs(shortStr));
+ BOOST_CHECK(callContractFunction("getX()") == encodeDyn(longStr));
+ BOOST_CHECK(callContractFunction("getX2()") == encodeDyn(longStr));
+ BOOST_CHECK(callContractFunction("unused()") == encodeArgs(2));
+}
+
+BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ mapping(string => uint) data;
+ function f() returns (uint) {
+ data["abc"] = 2;
+ return data["abc"];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(library_call)
+{
+ char const* sourceCode = R"(
+ library Lib { function m(uint x, uint y) returns (uint) { return x * y; } }
+ contract Test {
+ function f(uint x) returns (uint) {
+ return Lib.m(x, 9);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(33) * 9));
+}
+
+BOOST_AUTO_TEST_CASE(library_stray_values)
+{
+ char const* sourceCode = R"(
+ library Lib { function m(uint x, uint y) returns (uint) { return x * y; } }
+ contract Test {
+ function f(uint x) returns (uint) {
+ Lib;
+ Lib.m;
+ return x + 9;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(33)) == encodeArgs(u256(42)));
+}
+
+BOOST_AUTO_TEST_CASE(cross_contract_types)
+{
+ char const* sourceCode = R"(
+ contract Lib { struct S {uint a; uint b; } }
+ contract Test {
+ function f() returns (uint r) {
+ var x = Lib.S({a: 2, b: 3});
+ r = x.b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Test");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3)));
+}
+
+BOOST_AUTO_TEST_CASE(simple_throw)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function f(uint x) returns (uint) {
+ if (x > 10)
+ return x + 10;
+ else
+ throw;
+ return 2;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(11)) == encodeArgs(u256(21)));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs());
+}
+
+BOOST_AUTO_TEST_CASE(strings_in_struct)
+{
+ char const* sourceCode = R"(
+ contract buggystruct {
+ Buggy public bug;
+
+ struct Buggy {
+ uint first;
+ uint second;
+ uint third;
+ string last;
+ }
+
+ function buggystruct(){
+ bug = Buggy(10, 20, 30, "asdfghjkl");
+ }
+ function getFirst() returns (uint)
+ {
+ return bug.first;
+ }
+ function getSecond() returns (uint)
+ {
+ return bug.second;
+ }
+ function getThird() returns (uint)
+ {
+ return bug.third;
+ }
+ function getLast() returns (string)
+ {
+ return bug.last;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ string s = "asdfghjkl";
+ BOOST_CHECK(callContractFunction("getFirst()") == encodeArgs(u256(10)));
+ BOOST_CHECK(callContractFunction("getSecond()") == encodeArgs(u256(20)));
+ BOOST_CHECK(callContractFunction("getThird()") == encodeArgs(u256(30)));
+ BOOST_CHECK(callContractFunction("getLast()") == encodeDyn(s));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type)
+{
+ char const* sourceCode = R"(
+ contract A {
+ function f(uint16 input) constant returns (uint16[5] arr)
+ {
+ arr[0] = input;
+ arr[1] = ++input;
+ arr[2] = ++input;
+ arr[3] = ++input;
+ arr[4] = ++input;
+ }
+ }
+ contract B {
+ function f() returns (uint16[5] res, uint16[5] res2)
+ {
+ var a = new A();
+ res = a.f(2);
+ res2 = a.f(1000);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "B");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(
+ u256(2), u256(3), u256(4), u256(5), u256(6), // first return argument
+ u256(1000), u256(1001), u256(1002), u256(1003), u256(1004)) // second return argument
+ );
+}
+
+BOOST_AUTO_TEST_CASE(internal_types_in_library)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ function find(uint16[] storage _haystack, uint16 _needle) constant returns (uint)
+ {
+ for (uint i = 0; i < _haystack.length; ++i)
+ if (_haystack[i] == _needle)
+ return i;
+ return uint(-1);
+ }
+ }
+ contract Test {
+ mapping(string => uint16[]) data;
+ function f() returns (uint a, uint b)
+ {
+ data["abc"].length = 20;
+ data["abc"][4] = 9;
+ data["abc"][17] = 3;
+ a = Lib.find(data["abc"], 9);
+ b = Lib.find(data["abc"], 3);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(4), u256(17)));
+}
+
+BOOST_AUTO_TEST_CASE(using_library_structs)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ struct Data { uint a; uint[] b; }
+ function set(Data storage _s)
+ {
+ _s.a = 7;
+ _s.b.length = 20;
+ _s.b[19] = 8;
+ }
+ }
+ contract Test {
+ mapping(string => Lib.Data) data;
+ function f() returns (uint a, uint b)
+ {
+ Lib.set(data["abc"]);
+ a = data["abc"].a;
+ b = data["abc"].b[19];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7), u256(8)));
+}
+
+BOOST_AUTO_TEST_CASE(short_strings)
+{
+ // This test verifies that the byte array encoding that combines length and data works
+ // correctly.
+ char const* sourceCode = R"(
+ contract A {
+ bytes public data1 = "123";
+ bytes data2;
+ function lengthChange() returns (uint)
+ {
+ // store constant in short and long string
+ data1 = "123";
+ if (!equal(data1, "123")) return 1;
+ data2 = "12345678901234567890123456789012345678901234567890a";
+ if (data2[17] != "8") return 3;
+ if (data2.length != 51) return 4;
+ if (data2[data2.length - 1] != "a") return 5;
+ // change length: short -> short
+ data1.length = 5;
+ if (data1.length != 5) return 6;
+ data1[4] = "4";
+ if (data1[0] != "1") return 7;
+ if (data1[4] != "4") return 8;
+ // change length: short -> long
+ data1.length = 80;
+ if (data1.length != 80) return 9;
+ data1.length = 70;
+ if (data1.length != 70) return 9;
+ if (data1[0] != "1") return 10;
+ if (data1[4] != "4") return 11;
+ for (uint i = 0; i < data1.length; i ++)
+ data1[i] = byte(i * 3);
+ if (data1[4] != 4 * 3) return 12;
+ if (data1[67] != 67 * 3) return 13;
+ // change length: long -> short
+ data1.length = 22;
+ if (data1.length != 22) return 14;
+ if (data1[21] != byte(21 * 3)) return 15;
+ if (data1[2] != 2 * 3) return 16;
+ // change length: short -> shorter
+ data1.length = 19;
+ if (data1.length != 19) return 17;
+ if (data1[7] != byte(7 * 3)) return 18;
+ // and now again to original size
+ data1.length = 22;
+ if (data1.length != 22) return 19;
+ if (data1[21] != 0) return 20;
+ data1.length = 0;
+ data2.length = 0;
+ }
+ function copy() returns (uint) {
+ bytes memory x = "123";
+ bytes memory y = "012345678901234567890123456789012345678901234567890123456789";
+ bytes memory z = "1234567";
+ data1 = x;
+ data2 = y;
+ if (!equal(data1, x)) return 1;
+ if (!equal(data2, y)) return 2;
+ // lengthen
+ data1 = y;
+ if (!equal(data1, y)) return 3;
+ // shorten
+ data1 = x;
+ if (!equal(data1, x)) return 4;
+ // change while keeping short
+ data1 = z;
+ if (!equal(data1, z)) return 5;
+ // copy storage -> storage
+ data1 = x;
+ data2 = y;
+ // lengthen
+ data1 = data2;
+ if (!equal(data1, y)) return 6;
+ // shorten
+ data1 = x;
+ data2 = data1;
+ if (!equal(data2, x)) return 7;
+ bytes memory c = data2;
+ data1 = c;
+ if (!equal(data1, x)) return 8;
+ data1 = "";
+ data2 = "";
+ }
+ function deleteElements() returns (uint) {
+ data1 = "01234";
+ delete data1[2];
+ if (data1[2] != 0) return 1;
+ if (data1[0] != "0") return 2;
+ if (data1[3] != "3") return 3;
+ delete data1;
+ if (data1.length != 0) return 4;
+ }
+
+ function equal(bytes storage a, bytes memory b) internal returns (bool) {
+ if (a.length != b.length) return false;
+ for (uint i = 0; i < a.length; ++i) if (a[i] != b[i]) return false;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "A");
+ BOOST_CHECK(callContractFunction("data1()") == encodeDyn(string("123")));
+ BOOST_CHECK(callContractFunction("lengthChange()") == encodeArgs(u256(0)));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("deleteElements()") == encodeArgs(u256(0)));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+ BOOST_CHECK(callContractFunction("copy()") == encodeArgs(u256(0)));
+ BOOST_CHECK(m_state.storage(m_contractAddress).empty());
+}
+
+BOOST_AUTO_TEST_CASE(calldata_offset)
+{
+ // This tests a specific bug that was caused by not using the correct memory offset in the
+ // calldata unpacker.
+ char const* sourceCode = R"(
+ contract CB
+ {
+ address[] _arr;
+ string public last = "nd";
+ function CB(address[] guardians)
+ {
+ _arr = guardians;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "CB", encodeArgs(u256(0x20)));
+ BOOST_CHECK(callContractFunction("last()", encodeArgs()) == encodeDyn(string("nd")));
+}
+
+BOOST_AUTO_TEST_CASE(version_stamp_for_libraries)
+{
+ char const* sourceCode = "library lib {}";
+ m_optimize = true;
+ bytes runtimeCode = compileAndRun(sourceCode, 0, "lib");
+ BOOST_CHECK(runtimeCode.size() >= 8);
+ BOOST_CHECK_EQUAL(runtimeCode[0], int(eth::Instruction::PUSH6)); // might change once we switch to 1.x.x
+ BOOST_CHECK_EQUAL(runtimeCode[1], 3); // might change once we switch away from x.3.x
+ BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP));
+}
+
+BOOST_AUTO_TEST_CASE(contract_binary_dependencies)
+{
+ char const* sourceCode = R"(
+ contract A { function f() { new B(); } }
+ contract B { function f() { } }
+ contract C { function f() { new B(); } }
+ )";
+ compileAndRun(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
+{
+ char const* sourceCode = R"(
+ library lib {}
+ contract c {
+ function f(address x) returns (bool) {
+ return x.send(1);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "lib");
+ Address libraryAddress = m_contractAddress;
+ compileAndRun(sourceCode, 10, "c");
+ BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 10);
+ BOOST_CHECK_EQUAL(m_state.balance(libraryAddress), 0);
+ BOOST_CHECK(callContractFunction("f(address)", encodeArgs(u160(libraryAddress))) == encodeArgs(false));
+ BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 10);
+ BOOST_CHECK_EQUAL(m_state.balance(libraryAddress), 0);
+ BOOST_CHECK(callContractFunction("f(address)", encodeArgs(u160(m_contractAddress))) == encodeArgs(true));
+ BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 10);
+ BOOST_CHECK_EQUAL(m_state.balance(libraryAddress), 0);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function g() returns (uint a, uint b, uint c) {
+ a = 1; b = 2; c = 3;
+ }
+ function f() returns (bool) {
+ var (x, y, z) = g();
+ if (x != 1 || y != 2 || z != 3) return false;
+ var (, a,) = g();
+ if (a != 2) return false;
+ var (b,) = g();
+ if (b != 1) return false;
+ var (,c) = g();
+ if (c != 3) return false;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()", encodeArgs()) == encodeArgs(true));
+}
+
+BOOST_AUTO_TEST_CASE(tuples)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint[] data;
+ function g() internal returns (uint a, uint b, uint[] storage c) {
+ return (1, 2, data);
+ }
+ function h() external returns (uint a, uint b) {
+ return (5, 6);
+ }
+ function f() returns (uint) {
+ data.length = 1;
+ data[0] = 3;
+ uint a; uint b;
+ (a, b) = this.h();
+ if (a != 5 || b != 6) return 1;
+ uint[] storage c;
+ (a, b, c) = g();
+ if (a != 1 || b != 2 || c[0] != 3) return 2;
+ (a, b) = (b, a);
+ if (a != 2 || b != 1) return 3;
+ (a, , b, ) = (8, 9, 10, 11, 12);
+ if (a != 8 || b != 10) return 4;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(string_tuples)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (string, uint) {
+ return ("abc", 8);
+ }
+ function g() returns (string, string) {
+ return (h(), "def");
+ }
+ function h() returns (string) {
+ return ("abc",);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x40), u256(8), u256(3), string("abc")));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(0x40), u256(0x80), u256(3), string("abc"), u256(3), string("def")));
+}
+
+BOOST_AUTO_TEST_CASE(decayed_tuple)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ uint x = 1;
+ (x) = 2;
+ return x;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
+}
+
+BOOST_AUTO_TEST_CASE(destructuring_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint x = 7;
+ bytes data;
+ uint[] y;
+ uint[] arrayData;
+ function returnsArray() returns (uint[]) {
+ arrayData.length = 9;
+ arrayData[2] = 5;
+ arrayData[7] = 4;
+ return arrayData;
+ }
+ function f(bytes s) returns (uint) {
+ uint loc;
+ uint[] memory memArray;
+ (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2);
+ if (loc != 8) return 1;
+ if (x != 4) return 2;
+ if (y.length != 9) return 3;
+ if (y[2] != 5) return 4;
+ if (y[7] != 4) return 5;
+ if (data.length != s.length) return 6;
+ if (data[3] != s[3]) return 7;
+ if (arrayData[3] != 2) return 8;
+ (memArray, loc) = (arrayData, 3);
+ if (loc != 3) return 9;
+ if (memArray.length != arrayData.length) return 10;
+ bytes memory memBytes;
+ (x, memBytes, y[2], ) = (456, s, 789, 101112, 131415);
+ if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), string("abcde")) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(destructuring_assignment_wildcard)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ uint a;
+ uint b;
+ uint c;
+ (a,) = (1,);
+ if (a != 1) return 1;
+ (,b) = (2,3,4);
+ if (b != 4) return 2;
+ (, c,) = (5,6,7);
+ if (c != 6) return 3;
+ (a, b,) = (11, 12, 13);
+ if (a != 11 || b != 12) return 4;
+ (, a, b) = (11, 12, 13);
+ if (a != 12 || b != 13) return 5;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(lone_struct_array_type)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct s { uint a; uint b;}
+ function f() returns (uint) {
+ s[7][]; // This is only the type, should not have any effect
+ return 3;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3)));
+}
+
+BOOST_AUTO_TEST_CASE(create_memory_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct S { uint[2] a; bytes b; }
+ function f() returns (byte, uint, uint, byte) {
+ var x = new bytes(200);
+ x[199] = 'A';
+ var y = new uint[2][](300);
+ y[203][1] = 8;
+ var z = new S[](180);
+ z[170].a[1] = 4;
+ z[170].b = new bytes(102);
+ z[170].b[99] = 'B';
+ return (x[199], y[203][1], z[170].a[1], z[170].b[99]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(string("A"), u256(8), u256(4), string("B")));
+}
+
+BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes)
+{
+ // Computes binomial coefficients the chinese way
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint n, uint k) returns (uint) {
+ uint[][] memory rows = new uint[][](n + 1);
+ for (uint i = 1; i <= n; i++) {
+ rows[i] = new uint[](i);
+ rows[i][0] = rows[i][rows[i].length - 1] = 1;
+ for (uint j = 1; j < i - 1; j++)
+ rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j];
+ }
+ return rows[n][k - 1];
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(3), u256(1))) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))) == encodeArgs(u256(70)));
+}
+
+BOOST_AUTO_TEST_CASE(memory_overwrite)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (bytes x) {
+ x = "12345";
+ x[3] = 0x61;
+ x[0] = 0x62;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeDyn(string("b23a5")));
+}
+
+BOOST_AUTO_TEST_CASE(addmod_mulmod)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function test() returns (uint) {
+ // Note that this only works because computation on literals is done using
+ // unbounded integers.
+ if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7))
+ return 1;
+ if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7))
+ return 2;
+ return 0;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(string_allocation_bug)
+{
+ char const* sourceCode = R"(
+ contract Sample
+ {
+ struct s { uint16 x; uint16 y; string a; string b;}
+ s[2] public p;
+ function Sample() {
+ s memory m;
+ m.x = 0xbbbb;
+ m.y = 0xcccc;
+ m.a = "hello";
+ m.b = "world";
+ p[0] = m;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("p(uint256)") == encodeArgs(
+ u256(0xbbbb),
+ u256(0xcccc),
+ u256(0x80),
+ u256(0xc0),
+ u256(5),
+ string("hello"),
+ u256(5),
+ string("world")
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_on_int)
+{
+ char const* sourceCode = R"(
+ library D { function double(uint self) returns (uint) { return 2*self; } }
+ contract C {
+ using D for uint;
+ function f(uint a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(9)) == encodeArgs(u256(2 * 9)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_on_struct)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 3;
+ return x.mul(a);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(3 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(3 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_overload)
+{
+ char const* sourceCode = R"(
+ library D {
+ struct s { uint a; }
+ function mul(s storage self, uint x) returns (uint) { return self.a *= x; }
+ function mul(s storage self, bytes32 x) returns (bytes32) { }
+ }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ return x.mul(a);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_by_name)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ return x.mul({x: a});
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(bound_function_in_var)
+{
+ char const* sourceCode = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s public x;
+ function f(uint a) returns (uint) {
+ x.a = 6;
+ var g = x.mul;
+ return g({x: a});
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(7)) == encodeArgs(u256(6 * 7)));
+ BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(6 * 7)));
+}
+
+BOOST_AUTO_TEST_CASE(bound_function_to_string)
+{
+ char const* sourceCode = R"(
+ library D { function length(string memory self) returns (uint) { return bytes(self).length; } }
+ contract C {
+ using D for string;
+ string x;
+ function f() returns (uint) {
+ x = "abc";
+ return x.length();
+ }
+ function g() returns (uint) {
+ string memory s = "abc";
+ return s.length();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"D", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(3)));
+ BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(3)));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_strings)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string s = "doh";
+ function f() returns (string, string) {
+ string memory t = "ray";
+ string[3] memory x = [s, t, "mi"];
+ return (x[1], x[2]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x40), u256(0x80), u256(3), string("ray"), u256(2), string("mi")));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_strings_from_document)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint i) returns (string) {
+ string[4] memory x = ["This", "is", "an", "array"];
+ return (x[i]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(0x20), u256(4), string("This")));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(0x20), u256(2), string("is")));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(0x20), u256(2), string("an")));
+ BOOST_CHECK(callContractFunction("f(uint256)", u256(3)) == encodeArgs(u256(0x20), u256(5), string("array")));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_ints)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint x, uint y) {
+ x = 3;
+ y = 6;
+ uint[2] memory z = [x, y];
+ return (z[0], z[1]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(3, 6));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_index_access_ints)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ return ([1, 2, 3, 4][2]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(3));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_index_access_strings)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string public tester;
+ function f() returns (string) {
+ return (["abc", "def", "g"][0]);
+ }
+ function test() {
+ tester = f();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("test()") == encodeArgs());
+ BOOST_CHECK(callContractFunction("tester()") == encodeArgs(u256(0x20), u256(3), string("abc")));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_return)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint8[] tester;
+ function f() returns (uint8[5]) {
+ return ([1,2,3,4,5]);
+ }
+ function test() returns (uint8, uint8, uint8, uint8, uint8) {
+ tester = f();
+ return (tester[0], tester[1], tester[2], tester[3], tester[4]);
+ }
+
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(1, 2, 3, 4, 5));
+}
+
+BOOST_AUTO_TEST_CASE(inline_long_string_return)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (string) {
+ return (["somethingShort", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"][1]);
+ }
+ }
+ )";
+
+ string strLong = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeDyn(strLong));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_bytes_index_access)
+{
+ char const* sourceCode = R"(
+ contract C {
+ bytes16[] public data;
+ function f(bytes32 x) returns (byte) {
+ return x[2];
+ }
+ function g(bytes32 x) returns (uint) {
+ data = [x[0], x[1], x[2]];
+ data[0] = "12345";
+ return uint(data[0][4]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(bytes32)", "789") == encodeArgs("9"));
+ BOOST_CHECK(callContractFunction("g(bytes32)", "789") == encodeArgs(u256(int('5'))));
+ BOOST_CHECK(callContractFunction("data(uint256)", u256(1)) == encodeArgs("8"));
+}
+
+BOOST_AUTO_TEST_CASE(fixed_bytes_length_access)
+{
+ char const* sourceCode = R"(
+ contract C {
+ byte a;
+ function f(bytes32 x) returns (uint, uint, uint) {
+ return (x.length, bytes16(2).length, a.length + 7);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(bytes32)", "789") == encodeArgs(u256(32), u256(16), u256(8)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
new file mode 100644
index 00000000..0e814e56
--- /dev/null
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -0,0 +1,507 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity expression compiler.
+ */
+
+#include <string>
+
+#include <libdevcore/Log.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/codegen/ExpressionCompiler.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+/// Helper class that extracts the first expression in an AST.
+class FirstExpressionExtractor: private ASTVisitor
+{
+public:
+ FirstExpressionExtractor(ASTNode& _node): m_expression(nullptr) { _node.accept(*this); }
+ Expression* expression() const { return m_expression; }
+private:
+ virtual bool visit(Assignment& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(FunctionCall& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(MemberAccess& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(IndexAccess& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(Identifier& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); }
+ virtual bool visit(Literal& _expression) override { return checkExpression(_expression); }
+ bool checkExpression(Expression& _expression)
+ {
+ if (m_expression == nullptr)
+ m_expression = &_expression;
+ return false;
+ }
+private:
+ Expression* m_expression;
+};
+
+Declaration const& resolveDeclaration(
+ SourceUnit const& _sourceUnit,
+ vector<string> const& _namespacedName,
+ NameAndTypeResolver const& _resolver
+)
+{
+ ASTNode const* scope = &_sourceUnit;
+ // bracers are required, cause msvc couldnt handle this macro in for statement
+ for (string const& namePart: _namespacedName)
+ {
+ auto declarations = _resolver.resolveName(namePart, scope);
+ BOOST_REQUIRE(!declarations.empty());
+ BOOST_REQUIRE(scope = *declarations.begin());
+ }
+ BOOST_REQUIRE(scope);
+ return dynamic_cast<Declaration const&>(*scope);
+}
+
+bytes compileFirstExpression(
+ const string& _sourceCode,
+ vector<vector<string>> _functions = {},
+ vector<vector<string>> _localVariables = {},
+ vector<shared_ptr<MagicVariableDeclaration const>> _globalDeclarations = {}
+)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ try
+ {
+ ErrorList errors;
+ sourceUnit = Parser(errors).parse(make_shared<Scanner>(CharStream(_sourceCode)));
+ if (!sourceUnit)
+ return bytes();
+ }
+ catch(boost::exception const& _e)
+ {
+ auto msg = std::string("Parsing source code failed with: \n") + boost::diagnostic_information(_e);
+ BOOST_FAIL(msg);
+ }
+
+ vector<Declaration const*> declarations;
+ declarations.reserve(_globalDeclarations.size() + 1);
+ for (ASTPointer<Declaration const> const& variable: _globalDeclarations)
+ declarations.push_back(variable.get());
+
+ ErrorList errors;
+ NameAndTypeResolver resolver(declarations, errors);
+ resolver.registerDeclarations(*sourceUnit);
+
+ vector<ContractDefinition const*> inheritanceHierarchy;
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ ETH_TEST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract), "Resolving names failed");
+ inheritanceHierarchy = vector<ContractDefinition const*>(1, contract);
+ }
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ TypeChecker typeChecker(errors);
+ BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract));
+ }
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ FirstExpressionExtractor extractor(*contract);
+ BOOST_REQUIRE(extractor.expression() != nullptr);
+
+ CompilerContext context;
+ context.resetVisitedNodes(contract);
+ context.setInheritanceHierarchy(inheritanceHierarchy);
+ unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack
+ context.adjustStackOffset(parametersSize);
+ for (vector<string> const& variable: _localVariables)
+ context.addVariable(
+ dynamic_cast<VariableDeclaration const&>(resolveDeclaration(*sourceUnit, variable, resolver)),
+ parametersSize--
+ );
+
+ ExpressionCompiler(context).compile(*extractor.expression());
+
+ for (vector<string> const& function: _functions)
+ context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(
+ resolveDeclaration(*sourceUnit, function, resolver)
+ ));
+ bytes instructions = context.assembledObject().bytecode;
+ // debug
+ // cout << eth::disassemble(instructions) << endl;
+ return instructions;
+ }
+ BOOST_FAIL("No contract found in source.");
+ return bytes();
+}
+
+} // end anonymous namespace
+
+BOOST_AUTO_TEST_SUITE(SolidityExpressionCompiler)
+
+BOOST_AUTO_TEST_CASE(literal_true)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = true; }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(literal_false)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = false; }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x0});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(int_literal)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = 0x12345678901234567890; }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90,
+ 0x12, 0x34, 0x56, 0x78, 0x90});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test ()
+ {
+ var x = 1 wei;
+ }
+ })";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test ()
+ {
+ var x = 1 szabo;
+ }
+ })";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test ()
+ {
+ var x = 1 finney;
+ }
+ })";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test ()
+ {
+ var x = 1 ether;
+ }
+ })";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(comparison)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = (0x10aa < 0x11aa) != true; }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::PUSH2), 0x11, 0xaa,
+ byte(eth::Instruction::PUSH2), 0x10, 0xaa,
+ byte(eth::Instruction::LT),
+ byte(eth::Instruction::EQ),
+ byte(eth::Instruction::ISZERO)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(short_circuiting)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = true != (4 <= 8 + 10 || 9 != 2); }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x12, // 8 + 10
+ byte(eth::Instruction::PUSH1), 0x4,
+ byte(eth::Instruction::GT),
+ byte(eth::Instruction::ISZERO), // after this we have 4 <= 8 + 10
+ byte(eth::Instruction::DUP1),
+ byte(eth::Instruction::PUSH1), 0x11,
+ byte(eth::Instruction::JUMPI), // short-circuit if it is true
+ byte(eth::Instruction::POP),
+ byte(eth::Instruction::PUSH1), 0x2,
+ byte(eth::Instruction::PUSH1), 0x9,
+ byte(eth::Instruction::EQ),
+ byte(eth::Instruction::ISZERO), // after this we have 9 != 2
+ byte(eth::Instruction::JUMPDEST),
+ byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::EQ),
+ byte(eth::Instruction::ISZERO)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(arithmetics)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::PUSH1), 0x2,
+ byte(eth::Instruction::PUSH1), 0x3,
+ byte(eth::Instruction::PUSH1), 0x4,
+ byte(eth::Instruction::PUSH1), 0x5,
+ byte(eth::Instruction::PUSH1), 0x6,
+ byte(eth::Instruction::PUSH1), 0x7,
+ byte(eth::Instruction::PUSH1), 0x8,
+ byte(eth::Instruction::DUP10),
+ byte(eth::Instruction::XOR),
+ byte(eth::Instruction::AND),
+ byte(eth::Instruction::OR),
+ byte(eth::Instruction::SUB),
+ byte(eth::Instruction::ADD),
+ byte(eth::Instruction::MOD),
+ byte(eth::Instruction::DIV),
+ byte(eth::Instruction::MUL)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(unary_operators)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(int y) { var x = !(~+- y == 2); }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x2,
+ byte(eth::Instruction::DUP3),
+ byte(eth::Instruction::PUSH1), 0x0,
+ byte(eth::Instruction::SUB),
+ byte(eth::Instruction::NOT),
+ byte(eth::Instruction::EQ),
+ byte(eth::Instruction::ISZERO)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(unary_inc_dec)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
+
+ // Stack: a, x
+ bytes expectation({byte(eth::Instruction::DUP2),
+ byte(eth::Instruction::DUP1),
+ byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::ADD),
+ // Stack here: a x a (a+1)
+ byte(eth::Instruction::SWAP3),
+ byte(eth::Instruction::POP), // first ++
+ // Stack here: (a+1) x a
+ byte(eth::Instruction::DUP3),
+ byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::ADD),
+ // Stack here: (a+1) x a (a+2)
+ byte(eth::Instruction::SWAP3),
+ byte(eth::Instruction::POP),
+ // Stack here: (a+2) x a
+ byte(eth::Instruction::DUP3), // second ++
+ byte(eth::Instruction::XOR),
+ // Stack here: (a+2) x a^(a+2)
+ byte(eth::Instruction::DUP3),
+ byte(eth::Instruction::DUP1),
+ byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::SWAP1),
+ byte(eth::Instruction::SUB),
+ // Stack here: (a+2) x a^(a+2) (a+2) (a+1)
+ byte(eth::Instruction::SWAP4),
+ byte(eth::Instruction::POP), // first --
+ byte(eth::Instruction::XOR),
+ // Stack here: (a+1) x a^(a+2)^(a+2)
+ byte(eth::Instruction::DUP3),
+ byte(eth::Instruction::PUSH1), 0x1,
+ byte(eth::Instruction::SWAP1),
+ byte(eth::Instruction::SUB),
+ // Stack here: (a+1) x a^(a+2)^(a+2) a
+ byte(eth::Instruction::SWAP3),
+ byte(eth::Instruction::POP), // second ++
+ // Stack here: a x a^(a+2)^(a+2)
+ byte(eth::Instruction::DUP3), // will change
+ byte(eth::Instruction::XOR)});
+ // Stack here: a x a^(a+2)^(a+2)^a
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(assignment)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a, uint b) { (a += b) * 2; }"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
+
+ // Stack: a, b
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x2,
+ byte(eth::Instruction::DUP2),
+ byte(eth::Instruction::DUP4),
+ byte(eth::Instruction::ADD),
+ // Stack here: a b 2 a+b
+ byte(eth::Instruction::SWAP3),
+ byte(eth::Instruction::POP),
+ byte(eth::Instruction::DUP3),
+ // Stack here: a+b b 2 a+b
+ byte(eth::Instruction::MUL)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(function_call)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a, uint b) { a += g(a + 1, b) * 2; }\n"
+ " function g(uint a, uint b) returns (uint c) {}\n"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {{"test", "g"}},
+ {{"test", "f", "a"}, {"test", "f", "b"}});
+
+ // Stack: a, b
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x02,
+ byte(eth::Instruction::PUSH1), 0x0c,
+ byte(eth::Instruction::PUSH1), 0x01,
+ byte(eth::Instruction::DUP5),
+ byte(eth::Instruction::ADD),
+ // Stack here: a b 2 <ret label> (a+1)
+ byte(eth::Instruction::DUP4),
+ byte(eth::Instruction::PUSH1), 0x13,
+ byte(eth::Instruction::JUMP),
+ byte(eth::Instruction::JUMPDEST),
+ // Stack here: a b 2 g(a+1, b)
+ byte(eth::Instruction::MUL),
+ // Stack here: a b g(a+1, b)*2
+ byte(eth::Instruction::DUP3),
+ byte(eth::Instruction::ADD),
+ // Stack here: a b a+g(a+1, b)*2
+ byte(eth::Instruction::SWAP2),
+ byte(eth::Instruction::POP),
+ byte(eth::Instruction::DUP2),
+ byte(eth::Instruction::JUMPDEST)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(negative_literals_8bits)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { int8 x = -0x80; }\n"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80));
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(negative_literals_16bits)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() { int64 x = ~0xabc; }\n"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation(bytes({byte(eth::Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals)
+{
+ // first literal itself is too large for 256 bits but it fits after all constant operations
+ // have been applied
+ char const* sourceCode = "contract test {\n"
+ " function f() { var x = (0xffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }\n"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode);
+
+ bytes expectation(bytes({byte(eth::Instruction::PUSH1), 0xbf}));
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_CASE(blockhash)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f() {\n"
+ " block.blockhash(3);\n"
+ " }\n"
+ "}\n";
+ bytes code = compileFirstExpression(sourceCode, {}, {},
+ {make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block))});
+
+ bytes expectation({byte(eth::Instruction::PUSH1), 0x03,
+ byte(eth::Instruction::BLOCKHASH)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp
new file mode 100644
index 00000000..9a1c104d
--- /dev/null
+++ b/test/libsolidity/SolidityInterface.cpp
@@ -0,0 +1,165 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Unit tests for generating source interfaces for Solidity contracts.
+ */
+
+#include "../TestHelper.h"
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+class SolidityInterfaceChecker
+{
+public:
+ SolidityInterfaceChecker(): m_compilerStack(false) {}
+
+ /// Compiles the given code, generates the interface and parses that again.
+ ContractDefinition const& checkInterface(string const& _code, string const& _contractName = "")
+ {
+ m_code = _code;
+ ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed");
+ m_interface = m_compilerStack.metadata("", DocumentationType::ABISolidityInterface);
+ ETH_TEST_REQUIRE_NO_THROW(m_reCompiler.parse(m_interface), "Interface parsing failed");
+ return m_reCompiler.contractDefinition(_contractName);
+ }
+
+ string sourcePart(ASTNode const& _node) const
+ {
+ SourceLocation location = _node.location();
+ BOOST_REQUIRE(!location.isEmpty());
+ return m_interface.substr(location.start, location.end - location.start);
+ }
+
+protected:
+ string m_code;
+ string m_interface;
+ CompilerStack m_compilerStack;
+ CompilerStack m_reCompiler;
+};
+
+BOOST_FIXTURE_TEST_SUITE(SolidityInterface, SolidityInterfaceChecker)
+
+BOOST_AUTO_TEST_CASE(empty_contract)
+{
+ ContractDefinition const& contract = checkInterface("contract test {}");
+ BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}");
+}
+
+BOOST_AUTO_TEST_CASE(single_function)
+{
+ ContractDefinition const& contract = checkInterface(
+ "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ "}\n");
+ BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size());
+ BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()),
+ "function f(uint256 a)returns(uint256 d);");
+}
+
+BOOST_AUTO_TEST_CASE(single_constant_function)
+{
+ ContractDefinition const& contract = checkInterface(
+ "contract test { function f(uint a) constant returns(bytes1 x) { 1==2; } }");
+ BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size());
+ BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()),
+ "function f(uint256 a)constant returns(bytes1 x);");
+}
+
+BOOST_AUTO_TEST_CASE(multiple_functions)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ " function g(uint b) returns(uint e) { return b * 8; }\n"
+ "}\n";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ set<string> expectation({"function f(uint256 a)returns(uint256 d);",
+ "function g(uint256 b)returns(uint256 e);"});
+ BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size());
+ BOOST_CHECK(expectation == set<string>({sourcePart(*contract.definedFunctions().at(0)),
+ sourcePart(*contract.definedFunctions().at(1))}));
+}
+
+BOOST_AUTO_TEST_CASE(exclude_fallback_function)
+{
+ char const* sourceCode = "contract test { function() {} }";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}");
+}
+
+BOOST_AUTO_TEST_CASE(events)
+{
+ char const* sourceCode = "contract test {\n"
+ " function f(uint a) returns(uint d) { return a * 7; }\n"
+ " event e1(uint b, address indexed c); \n"
+ " event e2(); \n"
+ "}\n";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ // events should not appear in the Solidity Interface
+ BOOST_REQUIRE_EQUAL(0, contract.events().size());
+}
+
+BOOST_AUTO_TEST_CASE(inheritance)
+{
+ char const* sourceCode =
+ " contract Base { \n"
+ " function baseFunction(uint p) returns (uint i) { return p; } \n"
+ " event baseEvent(bytes32 indexed evtArgBase); \n"
+ " } \n"
+ " contract Derived is Base { \n"
+ " function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n"
+ " event derivedEvent(uint indexed evtArgDerived); \n"
+ " }";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ set<string> expectedFunctions({"function baseFunction(uint256 p)returns(uint256 i);",
+ "function derivedFunction(bytes32 p)returns(bytes32 i);"});
+ BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size());
+ BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0)),
+ sourcePart(*contract.definedFunctions().at(1))}));
+}
+
+BOOST_AUTO_TEST_CASE(libraries)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ struct Str { uint a; }
+ enum E { E1, E2 }
+ function f(uint[] x,Str storage y,E z) external;
+ }
+ )";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ BOOST_CHECK(contract.isLibrary());
+ set<string> expectedFunctions({"function f(uint256[] x,Lib.Str storage y,Lib.E z);"});
+ BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size());
+ BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0))}));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
new file mode 100644
index 00000000..3d0cc2cd
--- /dev/null
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -0,0 +1,3265 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Unit tests for the name and type resolution of the solidity parser.
+ */
+
+#include <string>
+
+#include <libdevcore/Log.h>
+#include <libdevcore/SHA3.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/analysis/SyntaxChecker.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/analysis/GlobalContext.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+pair<ASTPointer<SourceUnit>, std::shared_ptr<Error::Type const>>
+parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false)
+{
+ ErrorList errors;
+ Parser parser(errors);
+ ASTPointer<SourceUnit> sourceUnit;
+ // catch exceptions for a transition period
+ try
+ {
+ sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source)));
+ if(!sourceUnit)
+ return make_pair(sourceUnit, nullptr);
+
+ SyntaxChecker syntaxChecker(errors);
+ if (!syntaxChecker.checkSyntax(*sourceUnit))
+ return make_pair(sourceUnit, std::make_shared<Error::Type const>(errors[0]->type()));
+
+ std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>();
+ NameAndTypeResolver resolver(globalContext->declarations(), errors);
+ solAssert(Error::containsOnlyWarnings(errors), "");
+ resolver.registerDeclarations(*sourceUnit);
+
+ bool success = true;
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ globalContext->setCurrentContract(*contract);
+ resolver.updateDeclaration(*globalContext->currentThis());
+ resolver.updateDeclaration(*globalContext->currentSuper());
+ if (!resolver.resolveNamesAndTypes(*contract))
+ success = false;
+ }
+ if (success)
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ globalContext->setCurrentContract(*contract);
+ resolver.updateDeclaration(*globalContext->currentThis());
+
+ TypeChecker typeChecker(errors);
+ bool success = typeChecker.checkTypeRequirements(*contract);
+ BOOST_CHECK(success || !errors.empty());
+
+ }
+ for (auto const& currentError: errors)
+ {
+ if (
+ (_reportWarnings && currentError->type() == Error::Type::Warning) ||
+ (!_reportWarnings && currentError->type() != Error::Type::Warning)
+ )
+ return make_pair(sourceUnit, std::make_shared<Error::Type const>(currentError->type()));
+ }
+ }
+ catch (Error const& _e)
+ {
+ return make_pair(sourceUnit, std::make_shared<Error::Type const>(_e.type()));
+ }
+ catch (Exception const& /*_exception*/)
+ {
+ return make_pair(sourceUnit, nullptr);
+ }
+ return make_pair(sourceUnit, nullptr);
+}
+
+ASTPointer<SourceUnit> parseAndAnalyse(string const& _source)
+{
+ auto sourceAndError = parseAnalyseAndReturnError(_source);
+ BOOST_REQUIRE(!!sourceAndError.first);
+ BOOST_REQUIRE(!sourceAndError.second);
+ return sourceAndError.first;
+}
+
+bool success(string const& _source)
+{
+ return !parseAnalyseAndReturnError(_source).second;
+}
+
+Error::Type expectError(std::string const& _source, bool _warning = false)
+{
+ auto sourceAndError = parseAnalyseAndReturnError(_source, _warning);
+ BOOST_REQUIRE(!!sourceAndError.second);
+ BOOST_REQUIRE(!!sourceAndError.first);
+ return *sourceAndError.second;
+}
+
+static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source, unsigned index)
+{
+ ContractDefinition* contract;
+ unsigned counter = 0;
+ for (ASTPointer<ASTNode> const& node: _source->nodes())
+ if ((contract = dynamic_cast<ContractDefinition*>(node.get())) && counter == index)
+ return contract;
+
+ return nullptr;
+}
+
+static FunctionTypePointer retrieveFunctionBySignature(
+ ContractDefinition const* _contract,
+ std::string const& _signature
+)
+{
+ FixedHash<4> hash(dev::sha3(_signature));
+ return _contract->interfaceFunctions()[hash];
+}
+
+}
+
+BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVariable1;\n"
+ " function fun(uint256 arg1) { uint256 y; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
+{
+ char const* text = "contract test {\n"
+ " uint256 variable;\n"
+ " uint128 variable;\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(double_function_declaration)
+{
+ char const* text = "contract test {\n"
+ " function fun() { uint x; }\n"
+ " function fun() { uint x; }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration)
+{
+ char const* text = "contract test {\n"
+ " function f() { uint256 x; if (true) { uint256 x; } }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(name_shadowing)
+{
+ char const* text = "contract test {\n"
+ " uint256 variable;\n"
+ " function f() { uint32 variable ; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(name_references)
+{
+ char const* text = "contract test {\n"
+ " uint256 variable;\n"
+ " function f(uint256 arg) returns (uint out) { f(variable); test; out; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(undeclared_name)
+{
+ char const* text = "contract test {\n"
+ " uint256 variable;\n"
+ " function f(uint256 arg) { f(notfound); }"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(reference_to_later_declaration)
+{
+ char const* text = "contract test {\n"
+ " function g() { f(); }"
+ " function f() { }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive)
+{
+ char const* text = "contract test {\n"
+ " struct MyStructName {\n"
+ " address addr;\n"
+ " MyStructName x;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive)
+{
+ char const* text = "contract test {\n"
+ " struct MyStructName1 {\n"
+ " address addr;\n"
+ " uint256 count;\n"
+ " MyStructName2 x;\n"
+ " }\n"
+ " struct MyStructName2 {\n"
+ " MyStructName1 x;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive)
+{
+ char const* text = R"(
+ contract test {
+ struct s1 { uint a; }
+ struct s2 { s1 x; s1 y; }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping)
+{
+ char const* text = "contract test {\n"
+ " struct MyStructName1 {\n"
+ " address addr;\n"
+ " uint256 count;\n"
+ " mapping(uint => MyStructName1) x;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_inference_smoke_test)
+{
+ char const* text = "contract test {\n"
+ " function f(uint256 arg1, uint32 arg2) returns (bool ret) { var x = arg1 + arg2 == 8; ret = x; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_checking_return)
+{
+ char const* text = "contract test {\n"
+ " function f() returns (bool r) { return 1 >= 2; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number)
+{
+ char const* text = "contract test {\n"
+ " function f() returns (bool r1, bool r2) { return 1 >= 2; }"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type)
+{
+ char const* text = "contract test {\n"
+ " function f() returns (uint256 r) { return 1 >= 2; }"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(type_checking_function_call)
+{
+ char const* text = "contract test {\n"
+ " function f() returns (bool r) { return g(12, true) == 3; }\n"
+ " function g(uint256 a, bool b) returns (uint256 r) { }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_conversion_for_comparison)
+{
+ char const* text = "contract test {\n"
+ " function f() { uint32(2) == int64(2); }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid)
+{
+ char const* text = "contract test {\n"
+ " function f() { int32(2) == uint64(2); }"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
+{
+ char const* text = "contract test {\n"
+ " function f() returns (int256 r) { var x = int256(uint32(2)); return x; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(large_string_literal)
+{
+ char const* text = "contract test {\n"
+ " function f() { var x = \"123456789012345678901234567890123\"; }"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(balance)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint256 x = address(0).balance;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(balance_invalid)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " address(0).balance = 7;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_mapping)
+{
+ char const* text = "contract test {\n"
+ " struct str {\n"
+ " mapping(uint=>uint) map;\n"
+ " }\n"
+ " str data;"
+ " function fun() {\n"
+ " var a = data.map;\n"
+ " data.map = a;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_to_struct)
+{
+ char const* text = "contract test {\n"
+ " struct str {\n"
+ " mapping(uint=>uint) map;\n"
+ " }\n"
+ " str data;"
+ " function fun() {\n"
+ " var a = data;\n"
+ " data = a;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(returns_in_constructor)
+{
+ char const* text = "contract test {\n"
+ " function test() returns (uint a) {\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(forward_function_reference)
+{
+ char const* text = "contract First {\n"
+ " function fun() returns (bool ret) {\n"
+ " return Second(1).fun(1, true, 3) > 0;\n"
+ " }\n"
+ "}\n"
+ "contract Second {\n"
+ " function fun(uint a, bool b, uint c) returns (uint ret) {\n"
+ " if (First(2).fun() == true) return 1;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(comparison_bitop_precedence)
+{
+ char const* text = "contract First {\n"
+ " function fun() returns (bool ret) {\n"
+ " return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_no_implementation)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = "contract test {\n"
+ " function functionName(bytes32 input) returns (bytes32 out);\n"
+ "}\n";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
+ ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get());
+ BOOST_CHECK(contract);
+ BOOST_CHECK(!contract->annotation().isFullyImplemented);
+ BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented());
+}
+
+BOOST_AUTO_TEST_CASE(abstract_contract)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract base { function foo(); }
+ contract derived is base { function foo() {} }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
+ ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get());
+ ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get());
+ BOOST_CHECK(base);
+ BOOST_CHECK(!base->annotation().isFullyImplemented);
+ BOOST_CHECK(!base->definedFunctions()[0]->isImplemented());
+ BOOST_CHECK(derived);
+ BOOST_CHECK(derived->annotation().isFullyImplemented);
+ BOOST_CHECK(derived->definedFunctions()[0]->isImplemented());
+}
+
+BOOST_AUTO_TEST_CASE(abstract_contract_with_overload)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract base { function foo(bool); }
+ contract derived is base { function foo(uint) {} }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
+ ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get());
+ ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get());
+ BOOST_REQUIRE(base);
+ BOOST_CHECK(!base->annotation().isFullyImplemented);
+ BOOST_REQUIRE(derived);
+ BOOST_CHECK(!derived->annotation().isFullyImplemented);
+}
+
+BOOST_AUTO_TEST_CASE(create_abstract_contract)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract base { function foo(); }
+ contract derived {
+ base b;
+ function foo() { b = new base();}
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract BaseBase { function BaseBase(uint j); }
+ contract base is BaseBase { function foo(); }
+ contract derived is base {
+ function derived(uint i) BaseBase(i){}
+ function foo() {}
+ }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Parsing and name resolving failed");
+}
+
+BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract BaseBase { function BaseBase(uint j); }
+ contract base is BaseBase { function foo(); }
+ contract derived is base {
+ function derived(uint i) {}
+ function foo() {}
+ }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed");
+ std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes();
+ BOOST_CHECK_EQUAL(nodes.size(), 3);
+ ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get());
+ BOOST_CHECK(derived);
+ BOOST_CHECK(!derived->annotation().isFullyImplemented);
+}
+
+BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract base { function foo(); }
+ contract derived is base { function foo() {} }
+ contract wrong is derived { function foo(); }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_canonical_signature)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = "contract Test {\n"
+ " function foo(uint256 arg1, uint64 arg2, bool arg3) returns (uint256 ret) {\n"
+ " ret = arg1 + arg2;\n"
+ " }\n"
+ "}\n";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ auto functions = contract->definedFunctions();
+ BOOST_CHECK_EQUAL("foo(uint256,uint64,bool)", functions[0]->externalSignature());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = "contract Test {\n"
+ " function boo(uint arg1, bytes32 arg2, address arg3) returns (uint ret) {\n"
+ " ret = 5;\n"
+ " }\n"
+ "}\n";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ auto functions = contract->definedFunctions();
+ if (functions.empty())
+ continue;
+ BOOST_CHECK_EQUAL("boo(uint256,bytes32,address)", functions[0]->externalSignature());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(function_external_types)
+{
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract C {
+ uint a;
+ }
+ contract Test {
+ function boo(uint arg2, bool arg3, bytes8 arg4, bool[2] pairs, uint[] dynamic, C carg, address[] addresses) external returns (uint ret) {
+ ret = 5;
+ }
+ })";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ auto functions = contract->definedFunctions();
+ if (functions.empty())
+ continue;
+ BOOST_CHECK_EQUAL("boo(uint256,bool,bytes8,bool[2],uint256[],address,address[])", functions[0]->externalSignature());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(enum_external_type)
+{
+ // bug #1801
+ ASTPointer<SourceUnit> sourceUnit;
+ char const* text = R"(
+ contract Test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function boo(ActionChoices enumArg) external returns (uint ret) {
+ ret = 5;
+ }
+ })";
+ ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ auto functions = contract->definedFunctions();
+ if (functions.empty())
+ continue;
+ BOOST_CHECK_EQUAL("boo(uint8)", functions[0]->externalSignature());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion)
+{
+ char const* text = R"(
+ contract C {}
+ contract Test {
+ function externalCall() {
+ C arg;
+ this.g(arg);
+ }
+ function g (C c) external {}
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion)
+{
+ char const* text = R"(
+ contract C {}
+ contract Test {
+ function externalCall() {
+ address arg;
+ this.g(arg);
+ }
+ function g (C c) external {}
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion)
+{
+ char const* text = R"(
+ contract C {
+ uint a;
+ }
+ contract Test {
+ C a;
+ function g (C c) {}
+ function internalCall() {
+ g(a);
+ }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion)
+{
+ char const* text = R"(
+ contract C {
+ uint a;
+ }
+ contract Test {
+ address a;
+ function g (C c) {}
+ function internalCall() {
+ g(a);
+ }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(hash_collision_in_interface)
+{
+ char const* text = "contract test {\n"
+ " function gsf() {\n"
+ " }\n"
+ " function tgeo() {\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(inheritance_basic)
+{
+ char const* text = R"(
+ contract base { uint baseMember; struct BaseType { uint element; } }
+ contract derived is base {
+ BaseType data;
+ function f() { baseMember = 7; }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inheritance_diamond_basic)
+{
+ char const* text = R"(
+ contract root { function rootFunction() {} }
+ contract inter1 is root { function f() {} }
+ contract inter2 is root { function f() {} }
+ contract derived is root, inter2, inter1 {
+ function g() { f(); rootFunction(); }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(cyclic_inheritance)
+{
+ char const* text = R"(
+ contract A is B { }
+ contract B is A { }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(legal_override_direct)
+{
+ char const* text = R"(
+ contract B { function f() {} }
+ contract C is B { function f(uint i) {} }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(legal_override_indirect)
+{
+ char const* text = R"(
+ contract A { function f(uint a) {} }
+ contract B { function f() {} }
+ contract C is A, B { }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_visibility)
+{
+ char const* text = R"(
+ contract B { function f() internal {} }
+ contract C is B { function f() public {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(illegal_override_constness)
+{
+ char const* text = R"(
+ contract B { function f() constant {} }
+ contract C is B { function f() {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(complex_inheritance)
+{
+ char const* text = R"(
+ contract A { function f() { uint8 x = C(0).g(); } }
+ contract B { function f() {} function g() returns (uint8 r) {} }
+ contract C is A, B { }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_visibility)
+{
+ // The constructor of a base class should not be visible in the derived class
+ char const* text = R"(
+ contract A { function A() { } }
+ contract B is A { function f() { A x = A(0); } }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(overriding_constructor)
+{
+ // It is fine to "override" constructor of a base class since it is invisible
+ char const* text = R"(
+ contract A { function A() { } }
+ contract B is A { function A() returns (uint8 r) {} }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments)
+{
+ char const* text = R"(
+ contract A { function A(uint a) { } }
+ contract B is A { }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
+{
+ char const* text = R"(
+ contract A { function A(uint a) { } }
+ contract B is A { }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)
+{
+ char const* text = R"(
+ contract A { }
+ contract B is A {
+ function f() { A a = B(1); }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion)
+{
+ char const* text = R"(
+ contract A { }
+ contract B is A {
+ function f() { B b = A(1); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_invocation)
+{
+ char const* text = R"(
+ contract B {
+ function f() mod1(2, true) mod2("0123456") { }
+ modifier mod1(uint a, bool b) { if (b) _ }
+ modifier mod2(bytes7 a) { while (a == "1234567") _ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_function_modifier_type)
+{
+ char const* text = R"(
+ contract B {
+ function f() mod1(true) { }
+ modifier mod1(uint a) { if (a > 0) _ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters)
+{
+ char const* text = R"(
+ contract B {
+ function f(uint8 a) mod1(a, true) mod2(r) returns (bytes7 r) { }
+ modifier mod1(uint a, bool b) { if (b) _ }
+ modifier mod2(bytes7 a) { while (a == "1234567") _ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
+{
+ char const* text = R"(
+ contract B {
+ function f() mod(x) { uint x = 7; }
+ modifier mod(uint a) { if (a > 0) _ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(legal_modifier_override)
+{
+ char const* text = R"(
+ contract A { modifier mod(uint a) {} }
+ contract B is A { modifier mod(uint a) {} }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(illegal_modifier_override)
+{
+ char const* text = R"(
+ contract A { modifier mod(uint a) {} }
+ contract B is A { modifier mod(uint8 a) {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(modifier_overrides_function)
+{
+ char const* text = R"(
+ contract A { modifier mod(uint a) {} }
+ contract B is A { function mod(uint a) {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_overrides_modifier)
+{
+ char const* text = R"(
+ contract A { function mod(uint a) {} }
+ contract B is A { modifier mod(uint a) {} }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(modifier_returns_value)
+{
+ char const* text = R"(
+ contract A {
+ function f(uint a) mod(2) returns (uint r) {}
+ modifier mod(uint a) { return 7; }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(state_variable_accessors)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "uint256 public foo;\n"
+ "mapping(uint=>bytes4) public map;\n"
+ "mapping(uint=>mapping(uint=>bytes4)) public multiple_map;\n"
+ "}\n";
+
+ ASTPointer<SourceUnit> source;
+ ContractDefinition const* contract;
+ ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed");
+ BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr);
+ FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
+ BOOST_REQUIRE(function && function->hasDeclaration());
+ auto returnParams = function->returnParameterTypeNames(false);
+ BOOST_CHECK_EQUAL(returnParams.at(0), "uint256");
+ BOOST_CHECK(function->isConstant());
+
+ function = retrieveFunctionBySignature(contract, "map(uint256)");
+ BOOST_REQUIRE(function && function->hasDeclaration());
+ auto params = function->parameterTypeNames(false);
+ BOOST_CHECK_EQUAL(params.at(0), "uint256");
+ returnParams = function->returnParameterTypeNames(false);
+ BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
+ BOOST_CHECK(function->isConstant());
+
+ function = retrieveFunctionBySignature(contract, "multiple_map(uint256,uint256)");
+ BOOST_REQUIRE(function && function->hasDeclaration());
+ params = function->parameterTypeNames(false);
+ BOOST_CHECK_EQUAL(params.at(0), "uint256");
+ BOOST_CHECK_EQUAL(params.at(1), "uint256");
+ returnParams = function->returnParameterTypeNames(false);
+ BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
+ BOOST_CHECK(function->isConstant());
+}
+
+BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "uint256 foo;\n"
+ " function foo() {}\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(private_state_variable)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "uint256 private foo;\n"
+ "uint256 internal bar;\n"
+ "}\n";
+
+ ASTPointer<SourceUnit> source;
+ ContractDefinition const* contract;
+ ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed");
+ BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr);
+ FunctionTypePointer function;
+ function = retrieveFunctionBySignature(contract, "foo()");
+ BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
+ function = retrieveFunctionBySignature(contract, "bar()");
+ BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
+}
+
+BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor)
+{
+ // test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126
+ char const* text = "contract Parent {\n"
+ " uint256 public m_aMember;\n"
+ "}\n"
+ "contract Child is Parent{\n"
+ " function foo() returns (uint256) { return Parent.m_aMember; }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only)
+{
+ char const* sourceCode = R"(
+ contract test {
+ struct Data { uint[15] m_array; }
+ Data public data;
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member)
+{
+ char const* text = "contract Parent {\n"
+ " uint256 internal m_aMember;\n"
+ "}\n"
+ "contract Child is Parent{\n"
+ " function foo() returns (uint256) { return Parent.m_aMember; }\n"
+ "}\n";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1)
+{
+ char const* text = "contract Parent1 {\n"
+ " uint256 internal m_aMember1;\n"
+ "}\n"
+ "contract Parent2 is Parent1{\n"
+ " uint256 internal m_aMember2;\n"
+ "}\n"
+ "contract Child is Parent2{\n"
+ " function foo() returns (uint256) { return Parent2.m_aMember1; }\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2)
+{
+ char const* text = "contract Parent1 {\n"
+ " uint256 internal m_aMember1;\n"
+ "}\n"
+ "contract Parent2 is Parent1{\n"
+ " uint256 internal m_aMember2;\n"
+ "}\n"
+ "contract Child is Parent2{\n"
+ " function foo() returns (uint256) { return Child.m_aMember2; }\n"
+ " uint256 public m_aMember3;\n"
+ "}\n";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function)
+{
+ char const* text = R"(
+ contract C {
+ uint x;
+ function() { x = 2; }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function_with_arguments)
+{
+ char const* text = R"(
+ contract C {
+ uint x;
+ function(uint a) { x = 2; }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function_twice)
+{
+ char const* text = R"(
+ contract C {
+ uint x;
+ function() { x = 2; }
+ function() { x = 3; }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function_inheritance)
+{
+ char const* text = R"(
+ contract A {
+ uint x;
+ function() { x = 1; }
+ }
+ contract C is A {
+ function() { x = 2; }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(event)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint indexed a, bytes3 indexed s, bool indexed b);
+ function f() { e(2, "abc", true); }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(event_too_many_indexed)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d);
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(anonymous_event_four_indexed)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d) anonymous;
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(anonymous_event_too_many_indexed)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous;
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(event_call)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint a, bytes3 indexed s, bool indexed b);
+ function f() { e(2, "abc", true); }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(event_inheritance)
+{
+ char const* text = R"(
+ contract base {
+ event e(uint a, bytes3 indexed s, bool indexed b);
+ }
+ contract c is base {
+ function f() { e(2, "abc", true); }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(multiple_events_argument_clash)
+{
+ char const* text = R"(
+ contract c {
+ event e1(uint a, uint e1, uint e2);
+ event e2(uint a, uint e1, uint e2);
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(access_to_default_function_visibility)
+{
+ char const* text = R"(
+ contract c {
+ function f() {}
+ }
+ contract d {
+ function g() { c(0).f(); }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(access_to_internal_function)
+{
+ char const* text = R"(
+ contract c {
+ function f() internal {}
+ }
+ contract d {
+ function g() { c(0).f(); }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility)
+{
+ char const* text = R"(
+ contract c {
+ uint a;
+ }
+ contract d {
+ function g() { c(0).a(); }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(access_to_internal_state_variable)
+{
+ char const* text = R"(
+ contract c {
+ uint public a;
+ }
+ contract d {
+ function g() { c(0).a(); }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(error_count_in_named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
+ " function b() returns (uint r) { r = a({a: 1}); }\n"
+ "}\n";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(empty_in_named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
+ " function b() returns (uint r) { r = a({}); }\n"
+ "}\n";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
+ " function b() returns (uint r) { r = a({a: 1, a: 2}); }\n"
+ "}\n";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args)
+{
+ char const* sourceCode = "contract test {\n"
+ " function a(uint a, uint b) returns (uint r) { r = a + b; }\n"
+ " function b() returns (uint r) { r = a({a: 1, c: 2}); }\n"
+ "}\n";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_input_parameter)
+{
+ char const* text = R"(
+ contract test {
+ function f(uint){
+ }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
+{
+ char const* text = R"(
+ contract test {
+ function f() returns(bool){
+ }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
+{
+ char const* text = R"(
+ contract test {
+ function f(uint, uint k) returns(uint ret_k){
+ return k;
+ }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one)
+{
+ char const* text = R"(
+ contract test {
+ function f() returns(uint ret_k, uint){
+ return 5;
+ }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type)
+{
+ char const* sourceCode = "contract c { function f() { var (x) = f(); } }";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units)
+{
+ char const* sourceCodeFine = R"(
+ contract c {
+ function c ()
+ {
+ a = 115792089237316195423570985008687907853269984665640564039458;
+ }
+ uint256 a;
+ })";
+ ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCodeFine),
+ "Parsing and Resolving names failed");
+ char const* sourceCode = R"(
+ contract c {
+ function c ()
+ {
+ a = 115792089237316195423570985008687907853269984665640564039458 ether;
+ }
+ uint256 a;
+ })";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) { return 2 ** -3; }
+ })";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) { return 2 ** 10000000000; }
+ })";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(enum_member_access)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ choices = ActionChoices.GoStraight;
+ }
+ ActionChoices choices;
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(enum_member_access_accross_contracts)
+{
+ char const* text = R"(
+ contract Interface {
+ enum MyEnum { One, Two }
+ }
+ contract Impl {
+ function test() returns (Interface.MyEnum) {
+ return Interface.MyEnum.One;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(enum_invalid_member_access)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ choices = ActionChoices.RunAroundWavingYourHands;
+ }
+ ActionChoices choices;
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ a = uint256(ActionChoices.GoStraight);
+ b = uint64(ActionChoices.Sit);
+ }
+ uint256 a;
+ uint64 b;
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ a = 2;
+ b = ActionChoices(a);
+ }
+ uint256 a;
+ ActionChoices b;
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
+ function test()
+ {
+ a = ActionChoices.GoStraight;
+ b = ActionChoices.Sit;
+ }
+ uint256 a;
+ uint64 b;
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(enum_duplicate_values)
+{
+ char const* text = R"(
+ contract test {
+ enum ActionChoices { GoLeft, GoRight, GoLeft, Sit }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(private_visibility)
+{
+ char const* sourceCode = R"(
+ contract base {
+ function f() private {}
+ }
+ contract derived is base {
+ function g() { f(); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access)
+{
+ char const* sourceCode = R"(
+ contract base {
+ function f() private {}
+ }
+ contract derived is base {
+ function g() { base.f(); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_visibility)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function f() external {}
+ function g() { f(); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(external_base_visibility)
+{
+ char const* sourceCode = R"(
+ contract base {
+ function f() external {}
+ }
+ contract derived is base {
+ function g() { base.f(); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_argument_assign)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function f(uint a) external { a = 1; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_argument_increment)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function f(uint a) external { a++; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_argument_delete)
+{
+ char const* sourceCode = R"(
+ contract c {
+ function f(uint a) external { delete a; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
+{
+ char const* sourceCode = R"(
+ contract Vehicle {
+ function f(bytes _a) external returns (uint256 r) {r = 1;}
+ }
+ contract Bike is Vehicle {
+ function f(bytes _a) external returns (uint256 r) {r = 42;}
+ }
+ )";
+ ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCode), "Parsing and Name Resolving failed");
+}
+
+BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
+{
+ char const* text = R"(
+ contract c {
+ function f(uint a) { uint8[a] x; }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_with_different_types1)
+{
+ char const* text = R"(
+ contract c {
+ bytes a;
+ uint[] b;
+ function f() { b = a; }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_with_different_types2)
+{
+ char const* text = R"(
+ contract c {
+ uint32[] a;
+ uint8[] b;
+ function f() { b = a; }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible)
+{
+ char const* text = R"(
+ contract c {
+ uint32[] a;
+ uint8[] b;
+ function f() { a = b; }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_with_different_types_static_dynamic)
+{
+ char const* text = R"(
+ contract c {
+ uint32[] a;
+ uint8[80] b;
+ function f() { a = b; }
+ })";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static)
+{
+ char const* text = R"(
+ contract c {
+ uint[] a;
+ uint[80] b;
+ function f() { b = a; }
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int)
+{
+ char const* text = R"(
+ contract c {
+ uint8 a = 1000;
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string)
+{
+ char const* text = R"(
+ contract c {
+ uint a = "abc";
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName)
+{
+
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Int, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 8, 0)) == *make_shared<IntegerType>(8, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 16, 0)) == *make_shared<IntegerType>(16, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 24, 0)) == *make_shared<IntegerType>(24, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 32, 0)) == *make_shared<IntegerType>(32, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 40, 0)) == *make_shared<IntegerType>(40, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 48, 0)) == *make_shared<IntegerType>(48, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 56, 0)) == *make_shared<IntegerType>(56, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 64, 0)) == *make_shared<IntegerType>(64, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 72, 0)) == *make_shared<IntegerType>(72, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 80, 0)) == *make_shared<IntegerType>(80, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 88, 0)) == *make_shared<IntegerType>(88, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 96, 0)) == *make_shared<IntegerType>(96, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 104, 0)) == *make_shared<IntegerType>(104, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 112, 0)) == *make_shared<IntegerType>(112, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 120, 0)) == *make_shared<IntegerType>(120, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 128, 0)) == *make_shared<IntegerType>(128, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 136, 0)) == *make_shared<IntegerType>(136, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 144, 0)) == *make_shared<IntegerType>(144, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 152, 0)) == *make_shared<IntegerType>(152, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 160, 0)) == *make_shared<IntegerType>(160, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 168, 0)) == *make_shared<IntegerType>(168, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 176, 0)) == *make_shared<IntegerType>(176, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 184, 0)) == *make_shared<IntegerType>(184, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 192, 0)) == *make_shared<IntegerType>(192, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 200, 0)) == *make_shared<IntegerType>(200, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 208, 0)) == *make_shared<IntegerType>(208, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 216, 0)) == *make_shared<IntegerType>(216, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 224, 0)) == *make_shared<IntegerType>(224, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 232, 0)) == *make_shared<IntegerType>(232, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 240, 0)) == *make_shared<IntegerType>(240, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 248, 0)) == *make_shared<IntegerType>(248, IntegerType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 256, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Signed));
+
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UInt, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 8, 0)) == *make_shared<IntegerType>(8, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 16, 0)) == *make_shared<IntegerType>(16, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 24, 0)) == *make_shared<IntegerType>(24, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 32, 0)) == *make_shared<IntegerType>(32, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 40, 0)) == *make_shared<IntegerType>(40, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 48, 0)) == *make_shared<IntegerType>(48, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 56, 0)) == *make_shared<IntegerType>(56, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 64, 0)) == *make_shared<IntegerType>(64, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 72, 0)) == *make_shared<IntegerType>(72, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 80, 0)) == *make_shared<IntegerType>(80, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 88, 0)) == *make_shared<IntegerType>(88, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 96, 0)) == *make_shared<IntegerType>(96, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 104, 0)) == *make_shared<IntegerType>(104, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 112, 0)) == *make_shared<IntegerType>(112, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 120, 0)) == *make_shared<IntegerType>(120, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 128, 0)) == *make_shared<IntegerType>(128, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 136, 0)) == *make_shared<IntegerType>(136, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 144, 0)) == *make_shared<IntegerType>(144, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 152, 0)) == *make_shared<IntegerType>(152, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 160, 0)) == *make_shared<IntegerType>(160, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 168, 0)) == *make_shared<IntegerType>(168, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 176, 0)) == *make_shared<IntegerType>(176, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 184, 0)) == *make_shared<IntegerType>(184, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 192, 0)) == *make_shared<IntegerType>(192, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 200, 0)) == *make_shared<IntegerType>(200, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 208, 0)) == *make_shared<IntegerType>(208, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 216, 0)) == *make_shared<IntegerType>(216, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 224, 0)) == *make_shared<IntegerType>(224, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 232, 0)) == *make_shared<IntegerType>(232, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 240, 0)) == *make_shared<IntegerType>(240, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 248, 0)) == *make_shared<IntegerType>(248, IntegerType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 256, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned));
+
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Byte, 0, 0)) == *make_shared<FixedBytesType>(1));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 1, 0)) == *make_shared<FixedBytesType>(1));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 2, 0)) == *make_shared<FixedBytesType>(2));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 3, 0)) == *make_shared<FixedBytesType>(3));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 4, 0)) == *make_shared<FixedBytesType>(4));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 5, 0)) == *make_shared<FixedBytesType>(5));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 6, 0)) == *make_shared<FixedBytesType>(6));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 7, 0)) == *make_shared<FixedBytesType>(7));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 8, 0)) == *make_shared<FixedBytesType>(8));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 9, 0)) == *make_shared<FixedBytesType>(9));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 10, 0)) == *make_shared<FixedBytesType>(10));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 11, 0)) == *make_shared<FixedBytesType>(11));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 12, 0)) == *make_shared<FixedBytesType>(12));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 13, 0)) == *make_shared<FixedBytesType>(13));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 14, 0)) == *make_shared<FixedBytesType>(14));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 15, 0)) == *make_shared<FixedBytesType>(15));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 16, 0)) == *make_shared<FixedBytesType>(16));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 17, 0)) == *make_shared<FixedBytesType>(17));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 18, 0)) == *make_shared<FixedBytesType>(18));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 19, 0)) == *make_shared<FixedBytesType>(19));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 20, 0)) == *make_shared<FixedBytesType>(20));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 21, 0)) == *make_shared<FixedBytesType>(21));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 22, 0)) == *make_shared<FixedBytesType>(22));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 23, 0)) == *make_shared<FixedBytesType>(23));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 24, 0)) == *make_shared<FixedBytesType>(24));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 25, 0)) == *make_shared<FixedBytesType>(25));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 26, 0)) == *make_shared<FixedBytesType>(26));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 27, 0)) == *make_shared<FixedBytesType>(27));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 28, 0)) == *make_shared<FixedBytesType>(28));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 29, 0)) == *make_shared<FixedBytesType>(29));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 30, 0)) == *make_shared<FixedBytesType>(30));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared<FixedBytesType>(31));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared<FixedBytesType>(32));
+}
+
+BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1)
+{
+ char const* text = R"(
+ contract c {
+ bytes arr;
+ function f() { byte a = arr[0];}
+ })";
+ ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Type resolving failed");
+}
+
+BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
+{
+ char const* text = R"(
+ contract Foo {
+ function changeIt() { x = 9; }
+ uint constant x = 56;
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(complex_const_variable)
+{
+ //for now constant specifier is valid only for uint bytesXX and enums
+ char const* text = R"(
+ contract Foo {
+ mapping(uint => bool) constant mapVar;
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(uninitialized_const_variable)
+{
+ char const* text = R"(
+ contract Foo {
+ uint constant y;
+ })";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint) { return 1; }
+ function f(uint a) returns(uint) { return a; }
+ function g() returns(uint) { return f(3, 5); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function)
+{
+ // literal 1 can be both converted to uint and uint8, so the call is ambiguous.
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint8 a) returns(uint) { return a; }
+ function f(uint a) returns(uint) { return 2*a; }
+ function g() returns(uint) { return f(1); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a) returns(uint) { return 2 * a; }
+ function g() returns(uint) { var x = f; return x(7); }
+ }
+ )";
+ ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(sourceCode), "Type resolving failed");
+}
+
+BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint) { return 1; }
+ function f(uint a) returns(uint) { return 2 * a; }
+ function g() returns(uint) { var x = f; return x(7); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(external_types_clash)
+{
+ char const* sourceCode = R"(
+ contract base {
+ enum a { X }
+ function f(a) { }
+ }
+ contract test is base {
+ function f(uint8 a) { }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(override_changes_return_types)
+{
+ char const* sourceCode = R"(
+ contract base {
+ function f(uint a) returns (uint) { }
+ }
+ contract test is base {
+ function f(uint a) returns (uint8) { }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multiple_constructors)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test(uint a) { }
+ function test() {}
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(equal_overload)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function test(uint a) returns (uint b) { }
+ function test(uint a) external {}
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::DeclarationError);
+}
+
+BOOST_AUTO_TEST_CASE(uninitialized_var)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) { var x; return 2; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(string)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string s;
+ function f(string x) external { s = x; }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(string_index)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string s;
+ function f() { var a = s[2]; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(string_length)
+{
+ char const* sourceCode = R"(
+ contract C {
+ string s;
+ function f() { var a = s.length; }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound)
+{
+ char const* sourceCode = R"(
+ contract test {
+ int8 public i = -129;
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min)
+{
+ char const* sourceCode = R"(
+ contract test {
+ int8 public i = -128;
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound)
+{
+ char const* sourceCode = R"(
+ contract test {
+ int8 public j = 128;
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max)
+{
+ char const* sourceCode = R"(
+ contract test {
+ int8 public j = 127;
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint8 public x = -1;
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint8 public x = 700;
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(integer_boolean_operators)
+{
+ char const* sourceCode1 = R"(
+ contract test { function() { uint x = 1; uint y = 2; x || y; } }
+ )";
+ BOOST_CHECK(expectError(sourceCode1) == Error::Type::TypeError);
+ char const* sourceCode2 = R"(
+ contract test { function() { uint x = 1; uint y = 2; x && y; } }
+ )";
+ BOOST_CHECK(expectError(sourceCode2) == Error::Type::TypeError);
+ char const* sourceCode3 = R"(
+ contract test { function() { uint x = 1; !x; } }
+ )";
+ BOOST_CHECK(expectError(sourceCode3) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(reference_compare_operators)
+{
+ char const* sourceCode1 = R"(
+ contract test { bytes a; bytes b; function() { a == b; } }
+ )";
+ BOOST_CHECK(expectError(sourceCode1) == Error::Type::TypeError);
+ char const* sourceCode2 = R"(
+ contract test { struct s {uint a;} s x; s y; function() { x == y; } }
+ )";
+ BOOST_CHECK(expectError(sourceCode2) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(overwrite_memory_location_external)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint[] memory a) external {}
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(overwrite_storage_location_external)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint[] storage a) external {}
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(storage_location_local_variables)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ uint[] storage x;
+ uint[] memory y;
+ uint[] memory z;
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ mapping(uint=>uint)[] memory x;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint[] data;
+ function f(uint[] x) {
+ var dataRef = data;
+ dataRef = x;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint[] data;
+ uint8[] otherData;
+ function f() {
+ uint8[] storage x = otherData;
+ uint[] storage y = data;
+ y = x;
+ // note that data = otherData works
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint[] data;
+ function f() {
+ var x = data;
+ delete x;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint[] data;
+ function f(uint[] x) {
+ data = x;
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint[] storage x) private {
+ }
+ function g(uint[] x) {
+ f(x);
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint[] storage x) private {
+ g(x);
+ }
+ function g(uint[] x) {
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type)
+{
+ // Such an assignment is possible in storage, but not in memory
+ // (because it would incur an otherwise unnecessary copy).
+ // This requirement might be lifted, though.
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint8[] memory x) private {
+ uint[] memory y = x;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint) returns (string);
+ function g() {
+ var (x,) = this.f(2);
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() {
+ uint[] memory x;
+ x.length = 2;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(struct_constructor)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct S { uint a; bool x; }
+ function f() {
+ S memory s = S(1, true);
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(struct_constructor_nested)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct X { uint x1; uint x2; }
+ struct S { uint s1; uint[3] s2; X s3; }
+ function f() {
+ uint[3] memory s2;
+ S memory s = S(1, s2, X(4, 5));
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(struct_named_constructor)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct S { uint a; bool x; }
+ function f() {
+ S memory s = S({a: 1, x: true});
+ }
+ }
+ )";
+ BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
+}
+
+BOOST_AUTO_TEST_CASE(literal_strings)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() {
+ string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
+ string memory short = "123";
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() {
+ var x = 1.20;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() {
+ var x = 1e2;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
+{
+ char const* text = R"(
+ contract Test {
+ struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
+ S s;
+ function f() {
+ S memory x;
+ x.b[1];
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(string_bytes_conversion)
+{
+ char const* text = R"(
+ contract Test {
+ string s;
+ bytes b;
+ function h(string _s) external { bytes(_s).length; }
+ function i(string _s) internal { bytes(_s).length; }
+ function j() internal { bytes(s).length; }
+ function k(bytes _b) external { string(_b); }
+ function l(bytes _b) internal { string(_b); }
+ function m() internal { string(b); }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inheriting_from_library)
+{
+ char const* text = R"(
+ library Lib {}
+ contract Test is Lib {}
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(inheriting_library)
+{
+ char const* text = R"(
+ contract Test {}
+ library Lib is Test {}
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(library_having_variables)
+{
+ char const* text = R"(
+ library Lib { uint x; }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(valid_library)
+{
+ char const* text = R"(
+ library Lib { uint constant x = 9; }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(call_to_library_function)
+{
+ char const* text = R"(
+ library Lib {
+ uint constant public pimil = 3141592;
+ function min(uint x, uint y) returns (uint);
+ }
+ contract Test {
+ function f() {
+ uint t = Lib.min(Lib.pimil(), 7);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ function f() { var x = new Test(); }
+ }
+ )";
+ BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(array_out_of_bound_access)
+{
+ char const* text = R"(
+ contract c {
+ uint[2] dataArray;
+ function set5th() returns (bool) {
+ dataArray[5] = 2;
+ return true;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer)
+{
+ char const* text = R"(
+ contract C {
+ function f() { string x = "abc"; }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(non_initialized_references)
+{
+ char const* text = R"(
+ contract c
+ {
+ struct s{
+ uint a;
+ }
+ function f()
+ {
+ s x;
+ x.a = 2;
+ }
+ }
+ )";
+
+ BOOST_CHECK(expectError(text, true) == Error::Type::Warning);
+}
+
+BOOST_AUTO_TEST_CASE(sha3_with_large_integer_constant)
+{
+ char const* text = R"(
+ contract c
+ {
+ function f() { sha3(2**500); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(cyclic_binary_dependency)
+{
+ char const* text = R"(
+ contract A { function f() { new B(); } }
+ contract B { function f() { new C(); } }
+ contract C { function f() { new A(); } }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance)
+{
+ char const* text = R"(
+ contract A is B { }
+ contract B { function f() { new C(); } }
+ contract C { function f() { new A(); } }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail)
+{
+ char const* text = R"(
+ contract C { function f() { var (x,y); } }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fine)
+{
+ char const* text = R"(
+ contract C {
+ function three() returns (uint, uint, uint);
+ function two() returns (uint, uint);
+ function none();
+ function f() {
+ var (a,) = three();
+ var (b,c,) = two();
+ var (,d) = three();
+ var (,e,g) = two();
+ var (,,) = three();
+ var () = none();
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_1)
+{
+ char const* text = R"(
+ contract C {
+ function one() returns (uint);
+ function f() { var (a, b, ) = one(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2)
+{
+ char const* text = R"(
+ contract C {
+ function one() returns (uint);
+ function f() { var (a, , ) = one(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3)
+{
+ char const* text = R"(
+ contract C {
+ function one() returns (uint);
+ function f() { var (, , a) = one(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4)
+{
+ char const* text = R"(
+ contract C {
+ function one() returns (uint);
+ function f() { var (, a, b) = one(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(tuples)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint a = (1);
+ var (b,) = (1,);
+ var (c,d) = (1, 2 + a);
+ var (e,) = (1, 2, b);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(tuples_empty_components)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ (1,,2);
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5)
+{
+ char const* text = R"(
+ contract C {
+ function one() returns (uint);
+ function f() { var (,) = one(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6)
+{
+ char const* text = R"(
+ contract C {
+ function two() returns (uint, uint);
+ function f() { var (a, b, c) = two(); }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
+{
+ char const* text = R"(
+ contract C {
+ struct R { uint[10][10] y; }
+ struct S { uint a; uint b; uint[20][20][20] c; R d; }
+ S data;
+ function f() {
+ C.S x = data;
+ C.S memory y;
+ C.S[10] memory z;
+ C.S[10];
+ y.a = 2;
+ x.c[1][2][3] = 9;
+ x.d.y[2][2] = 3;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_library)
+{
+ char const* text = R"(
+ library D { }
+ contract C {
+ using D for uint;
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_not_library)
+{
+ char const* text = R"(
+ contract D { }
+ contract C {
+ using D for uint;
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_exists)
+{
+ char const* text = R"(
+ library D { function double(uint self) returns (uint) { return 2*self; } }
+ contract C {
+ using D for uint;
+ function f(uint a) {
+ a.double;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_on_int)
+{
+ char const* text = R"(
+ library D { function double(uint self) returns (uint) { return 2*self; } }
+ contract C {
+ using D for uint;
+ function f(uint a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_function_on_struct)
+{
+ char const* text = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s x;
+ function f(uint a) returns (uint) {
+ return x.mul(a);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_overload)
+{
+ char const* text = R"(
+ library D {
+ struct s { uint a; }
+ function mul(s storage self, uint x) returns (uint) { return self.a *= x; }
+ function mul(s storage self, bytes32 x) returns (bytes32) { }
+ }
+ contract C {
+ using D for D.s;
+ D.s x;
+ function f(uint a) returns (uint) {
+ return x.mul(a);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_by_name)
+{
+ char const* text = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s x;
+ function f(uint a) returns (uint) {
+ return x.mul({x: a});
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for_mismatch)
+{
+ char const* text = R"(
+ library D { function double(bytes32 self) returns (uint) { return 2; } }
+ contract C {
+ using D for uint;
+ function f(uint a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(using_for_not_used)
+{
+ // This is an error because the function is only bound to uint.
+ // Had it been bound to *, it would have worked.
+ char const* text = R"(
+ library D { function double(uint self) returns (uint) { return 2; } }
+ contract C {
+ using D for uint;
+ function f(uint16 a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch)
+{
+ // Bound to a, but self type does not match.
+ char const* text = R"(
+ library D { function double(bytes32 self) returns (uint) { return 2; } }
+ contract C {
+ using D for *;
+ function f(uint a) returns (uint) {
+ return a.double();
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(bound_function_in_var)
+{
+ char const* text = R"(
+ library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } }
+ contract C {
+ using D for D.s;
+ D.s x;
+ function f(uint a) returns (uint) {
+ var g = x.mul;
+ return g({x: a});
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(create_memory_arrays)
+{
+ char const* text = R"(
+ library L {
+ struct R { uint[10][10] y; }
+ struct S { uint a; uint b; uint[20][20][20] c; R d; }
+ }
+ contract C {
+ function f(uint size) {
+ L.S[][] memory x = new L.S[][](10);
+ var y = new uint[](20);
+ var z = new bytes(size);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_in_memory_array)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint size) {
+ var x = new mapping(uint => uint)[](4);
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(new_for_non_array)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint size) {
+ var x = new uint(7);
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint size) {
+ var x = new uint[]();
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(function_overload_array_type)
+{
+ char const* text = R"(
+ contract M {
+ function f(uint[] values);
+ function f(int[] values);
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint) {
+ uint8 x = 7;
+ uint16 y = 8;
+ uint32 z = 9;
+ uint32[3] memory ending = [x, y, z];
+ return (ending[1]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion_strings)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (string) {
+ string memory x = "Hello";
+ string memory y = "World";
+ string[2] memory z = [x, y];
+ return (z[0]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_const_int_conversion)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint) {
+ uint8[4] memory z = [1,2,3,5];
+ return (z[0]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_const_string_conversion)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (string) {
+ string[2] memory z = ["Hello", "World"];
+ return (z[0]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (uint) {
+ return ([4,5,6][1]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type_strings)
+{
+ char const* text = R"(
+ contract C {
+ function f() returns (string) {
+ return (["foo", "man", "choo"][1]);
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_struct_declaration_arrays)
+{
+ char const* text = R"(
+ contract C {
+ struct S {
+ uint a;
+ string b;
+ }
+ function f() {
+ S[2] memory x = [S({a: 1, b: "fish"}), S({a: 2, b: "fish"})];
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint[3] x = [45, 'foo', true];
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(dynamic_inline_array)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint8[4][4] memory dyn = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]];
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(lvalues_as_inline_array)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ [1, 2, 3]++;
+ [1, 2, 3] = [4, 5, 6];
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(break_not_in_loop)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ if (true)
+ break;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
+}
+
+BOOST_AUTO_TEST_CASE(continue_not_in_loop)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ if (true)
+ continue;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
+}
+
+BOOST_AUTO_TEST_CASE(continue_not_in_loop_2)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ while (true)
+ {
+ }
+ continue;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
+}
+
+BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ true ? true : 2;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint x;
+ uint y;
+ (true ? x : y) = 1;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct)
+{
+ char const* text = R"(
+ contract C {
+ struct s1 {
+ uint x;
+ }
+ struct s2 {
+ uint x;
+ }
+ function f() {
+ s1 x;
+ s2 y;
+ true ? x : y;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type)
+{
+ char const* text = R"(
+ contract C {
+ function x(bool) {}
+ function y() {}
+
+ function f() {
+ true ? x : y;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum)
+{
+ char const* text = R"(
+ contract C {
+ enum small { A, B, C, D }
+ enum big { A, B, C, D }
+
+ function f() {
+ small x;
+ big y;
+
+ true ? x : y;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping)
+{
+ char const* text = R"(
+ contract C {
+ mapping(uint8 => uint8) table1;
+ mapping(uint32 => uint8) table2;
+
+ function f() {
+ true ? table1 : table2;
+ }
+ }
+ )";
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(conditional_with_all_types)
+{
+ char const* text = R"(
+ contract C {
+ struct s1 {
+ uint x;
+ }
+ s1 struct_x;
+ s1 struct_y;
+
+ function fun_x() {}
+ function fun_y() {}
+
+ enum small { A, B, C, D }
+
+ mapping(uint8 => uint8) table1;
+ mapping(uint8 => uint8) table2;
+
+ function f() {
+ // integers
+ uint x;
+ uint y;
+ true ? x : y;
+
+ // integer constants
+ true ? 1 : 3;
+
+ // string literal
+ true ? "hello" : "world";
+
+ // bool
+ true ? true : false;
+
+ // real is not there yet.
+
+ // array
+ byte[2] memory a;
+ byte[2] memory b;
+ true ? a : b;
+
+ bytes memory e;
+ bytes memory f;
+ true ? e : f;
+
+ // fixed bytes
+ bytes2 c;
+ bytes2 d;
+ true ? c : d;
+
+ // contract doesn't fit in here
+
+ // struct
+ true ? struct_x : struct_y;
+
+ // function
+ true ? fun_x : fun_y;
+
+ // enum
+ small enum_x;
+ small enum_y;
+ true ? enum_x : enum_y;
+
+ // tuple
+ true ? (1, 2) : (3, 4);
+
+ // mapping
+ true ? table1 : table2;
+
+ // typetype
+ true ? uint32(1) : uint32(2);
+
+ // modifier doesn't fit in here
+
+ // magic doesn't fit in here
+
+ // module doesn't fit in here
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(constructor_call_invalid_arg_count)
+{
+ // This caused a segfault in an earlier version
+ char const* text = R"(
+ contract C {
+ function C(){}
+ }
+ contract D is C {
+ function D() C(5){}
+ }
+ )";
+
+ BOOST_CHECK(expectError(text) == Error::Type::TypeError);
+}
+
+BOOST_AUTO_TEST_CASE(index_access_for_bytes)
+{
+ char const* text = R"(
+ contract C {
+ bytes20 x;
+ function f(bytes16 b) {
+ b[uint(x[2])];
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(uint7_and_uintM_as_identifier)
+{
+ char const* text = R"(
+ contract test {
+ string uintM = "Hello 4 you";
+ function f() {
+ uint8 uint7 = 3;
+ uint7 = 5;
+ string memory intM;
+ uint bytesM = 21;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(varM_disqualified_as_keyword)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ uintM something = 3;
+ intM should = 4;
+ bytesM fail = "now";
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(long_uint_variable_fails)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ uint99999999999999999999999999 something = 3;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(bytes10abc_is_identifier)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ bytes32 bytes10abc = "abc";
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(int10abc_is_identifier)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ uint uint10abc = 3;
+ int int10abc = 4;
+ }
+ }
+ )";
+ BOOST_CHECK(success(text));
+}
+
+BOOST_AUTO_TEST_CASE(invalid_fixed_types)
+{
+ char const* text = R"(
+ contract test {
+ function f() {
+ fixed0x7 a = .3;
+ fixed99999999999999999999999999999999999999x7 b = 9.5;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value)
+{
+ char const* text = R"(
+ library L { function l() {} }
+ contract test {
+ function f() {
+ L.l.value;
+ }
+ }
+ )";
+ BOOST_CHECK(!success(text));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
new file mode 100644
index 00000000..8c0c2098
--- /dev/null
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -0,0 +1,581 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @author Lefteris Karapetsas <lefteris@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity compiler JSON Interface output.
+ */
+
+#include "../TestHelper.h"
+#include <string>
+#include <json/json.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libdevcore/Exceptions.h>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+class DocumentationChecker
+{
+public:
+ DocumentationChecker(): m_compilerStack(false) {}
+
+ void checkNatspec(
+ std::string const& _code,
+ std::string const& _expectedDocumentationString,
+ bool _userDocumentation
+ )
+ {
+ std::string generatedDocumentationString;
+ ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed");
+
+ if (_userDocumentation)
+ generatedDocumentationString = m_compilerStack.metadata("", DocumentationType::NatspecUser);
+ else
+ generatedDocumentationString = m_compilerStack.metadata("", DocumentationType::NatspecDev);
+ Json::Value generatedDocumentation;
+ m_reader.parse(generatedDocumentationString, generatedDocumentation);
+ Json::Value expectedDocumentation;
+ m_reader.parse(_expectedDocumentationString, expectedDocumentation);
+ BOOST_CHECK_MESSAGE(
+ expectedDocumentation == generatedDocumentation,
+ "Expected " << _expectedDocumentationString <<
+ "\n but got:\n" << generatedDocumentationString
+ );
+ }
+
+ void expectNatspecError(std::string const& _code)
+ {
+ BOOST_CHECK(!m_compilerStack.parse(_code));
+ BOOST_REQUIRE(Error::containsErrorOfType(m_compilerStack.errors(), Error::Type::DocstringParsingError));
+ }
+
+private:
+ CompilerStack m_compilerStack;
+ Json::Reader m_reader;
+};
+
+BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker)
+
+BOOST_AUTO_TEST_CASE(user_basic_test)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @notice Multiplies `a` by 7\n"
+ " function mul(uint a) returns(uint d) { return a * 7; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(dev_and_user_basic_test)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @notice Multiplies `a` by 7\n"
+ " /// @dev Multiplies a number by 7\n"
+ " function mul(uint a) returns(uint d) { return a * 7; }\n"
+ "}\n";
+
+ char const* devNatspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7\"\n"
+ " }\n"
+ " }\n"
+ "}}";
+
+ char const* userNatspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}"
+ "}}";
+
+ checkNatspec(sourceCode, devNatspec, false);
+ checkNatspec(sourceCode, userNatspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(user_multiline_comment)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @notice Multiplies `a` by 7\n"
+ " /// and then adds `b`\n"
+ " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n"
+ " {\n"
+ " return (a * 7) + b;\n"
+ " }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(user_multiple_functions)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @notice Multiplies `a` by 7 and then adds `b`\n"
+ " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n"
+ " {\n"
+ " return (a * 7) + b;\n"
+ " }\n"
+ "\n"
+ " /// @notice Divides `input` by `div`\n"
+ " function divide(uint input, uint div) returns(uint d)\n"
+ " {\n"
+ " return input / div;\n"
+ " }\n"
+ " /// @notice Subtracts 3 from `input`\n"
+ " function sub(int input) returns(int d)\n"
+ " {\n"
+ " return input - 3;\n"
+ " }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"},"
+ " \"divide(uint256,uint256)\":{ \"notice\": \"Divides `input` by `div`\"},"
+ " \"sub(int256)\":{ \"notice\": \"Subtracts 3 from `input`\"}"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(user_empty_contract)
+{
+ char const* sourceCode = "contract test {\n"
+ "}\n";
+
+ char const* natspec = "{\"methods\":{} }";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(dev_and_user_no_doc)
+{
+ char const* sourceCode = "contract test {\n"
+ " function mul(uint a) returns(uint d) { return a * 7; }\n"
+ " function sub(int input) returns(int d)\n"
+ " {\n"
+ " return input - 3;\n"
+ " }\n"
+ "}\n";
+
+ char const* devNatspec = "{\"methods\":{}}";
+ char const* userNatspec = "{\"methods\":{}}";
+
+ checkNatspec(sourceCode, devNatspec, false);
+ checkNatspec(sourceCode, userNatspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev\n"
+ " /// Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter\n"
+ " /// @param second Documentation for the second parameter\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \" Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " }\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_multiple_params)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter\n"
+ " /// @param second Documentation for the second parameter\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " }\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter starts here.\n"
+ " /// Since it's a really complicated parameter we need 2 lines\n"
+ " /// @param second Documentation for the second parameter\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " }\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_multiple_functions)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter\n"
+ " /// @param second Documentation for the second parameter\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ " \n"
+ " /// @dev Divides 2 numbers\n"
+ " /// @param input Documentation for the input parameter\n"
+ " /// @param div Documentation for the div parameter\n"
+ " function divide(uint input, uint div) returns(uint d)\n"
+ " {\n"
+ " return input / div;\n"
+ " }\n"
+ " /// @dev Subtracts 3 from `input`\n"
+ " /// @param input Documentation for the input parameter\n"
+ " function sub(int input) returns(int d)\n"
+ " {\n"
+ " return input - 3;\n"
+ " }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " }\n"
+ " },\n"
+ " \"divide(uint256,uint256)\":{ \n"
+ " \"details\": \"Divides 2 numbers\",\n"
+ " \"params\": {\n"
+ " \"input\": \"Documentation for the input parameter\",\n"
+ " \"div\": \"Documentation for the div parameter\"\n"
+ " }\n"
+ " },\n"
+ " \"sub(int256)\":{ \n"
+ " \"details\": \"Subtracts 3 from `input`\",\n"
+ " \"params\": {\n"
+ " \"input\": \"Documentation for the input parameter\"\n"
+ " }\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_return)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter starts here.\n"
+ " /// Since it's a really complicated parameter we need 2 lines\n"
+ " /// @param second Documentation for the second parameter\n"
+ " /// @return The result of the multiplication\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " },\n"
+ " \"return\": \"The result of the multiplication\"\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter starts here.\n"
+ " /// Since it's a really complicated parameter we need 2 lines\n"
+ " /// @param second Documentation for the second parameter\n"
+ " /// @return\n"
+ " /// The result of the multiplication\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " },\n"
+ " \"return\": \" The result of the multiplication\"\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+
+BOOST_AUTO_TEST_CASE(dev_multiline_return)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter starts here.\n"
+ " /// Since it's a really complicated parameter we need 2 lines\n"
+ " /// @param second Documentation for the second parameter\n"
+ " /// @return The result of the multiplication\n"
+ " /// and cookies with nutella\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " },\n"
+ " \"return\": \"The result of the multiplication and cookies with nutella\"\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_multiline_comment)
+{
+ char const* sourceCode = "contract test {\n"
+ " /**\n"
+ " * @dev Multiplies a number by 7 and adds second parameter\n"
+ " * @param a Documentation for the first parameter starts here.\n"
+ " * Since it's a really complicated parameter we need 2 lines\n"
+ " * @param second Documentation for the second parameter\n"
+ " * @return The result of the multiplication\n"
+ " * and cookies with nutella\n"
+ " */"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ "\"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
+ " \"params\": {\n"
+ " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
+ " \"second\": \"Documentation for the second parameter\"\n"
+ " },\n"
+ " \"return\": \"The result of the multiplication and cookies with nutella\"\n"
+ " }\n"
+ "}}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_contract_no_doc)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Mul function\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ " \"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Mul function\"\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_contract_doc)
+{
+ char const* sourceCode = " /// @author Lefteris\n"
+ " /// @title Just a test contract\n"
+ "contract test {\n"
+ " /// @dev Mul function\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ " \"author\": \"Lefteris\","
+ " \"title\": \"Just a test contract\","
+ " \"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Mul function\"\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(dev_author_at_function)
+{
+ char const* sourceCode = " /// @author Lefteris\n"
+ " /// @title Just a test contract\n"
+ "contract test {\n"
+ " /// @dev Mul function\n"
+ " /// @author John Doe\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ char const* natspec = "{"
+ " \"author\": \"Lefteris\","
+ " \"title\": \"Just a test contract\","
+ " \"methods\":{"
+ " \"mul(uint256,uint256)\":{ \n"
+ " \"details\": \"Mul function\",\n"
+ " \"author\": \"John Doe\",\n"
+ " }\n"
+ " }\n"
+ "}";
+
+ checkNatspec(sourceCode, natspec, false);
+}
+
+BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
+{
+ char const* sourceCode = R"(
+ contract test {
+ /// I do something awesome
+ function mul(uint a) returns(uint d) { return a * 7; }
+ }
+ )";
+
+
+ char const* natspec = R"ABCDEF(
+ {
+ "methods" : {
+ "mul(uint256)" : {
+ "notice" : "I do something awesome"
+ }
+ }
+ }
+ )ABCDEF";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
+{
+ char const* sourceCode = R"(
+ contract test {
+ /// I do something awesome
+ /// which requires two lines to explain
+ function mul(uint a) returns(uint d) { return a * 7; }
+ }
+ )";
+
+ char const* natspec = R"ABCDEF(
+ {
+ "methods" : {
+ "mul(uint256)" : {
+ "notice" : "I do something awesome which requires two lines to explain"
+ }
+ }
+ }
+ )ABCDEF";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(empty_comment)
+{
+ char const* sourceCode = R"(
+ //
+ contract test
+ {}
+ )";
+ char const* natspec = R"ABCDEF(
+ {
+ "methods" : {}
+ }
+ )ABCDEF";
+
+ checkNatspec(sourceCode, natspec, true);
+}
+
+BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
+{
+ char const* sourceCode = " /// @author Lefteris\n"
+ " /// @title Just a test contract\n"
+ "contract test {\n"
+ " /// @dev Mul function\n"
+ " /// @title I really should not be here\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ expectNatspecError(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
+{
+ char const* sourceCode = "contract test {\n"
+ " /// @dev Multiplies a number by 7 and adds second parameter\n"
+ " /// @param a Documentation for the first parameter\n"
+ " /// @param not_existing Documentation for the second parameter\n"
+ " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
+ "}\n";
+
+ expectNatspecError(sourceCode);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
new file mode 100644
index 00000000..6aa90973
--- /dev/null
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -0,0 +1,1181 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Tests for the Solidity optimizer.
+ */
+
+#include <string>
+#include <tuple>
+#include <memory>
+#include <boost/test/unit_test.hpp>
+#include <boost/lexical_cast.hpp>
+#include <test/libsolidity/solidityExecutionFramework.h>
+#include <libevmasm/CommonSubexpressionEliminator.h>
+#include <libevmasm/ControlFlowGraph.h>
+#include <libevmasm/Assembly.h>
+#include <libevmasm/BlockDeduplicator.h>
+
+using namespace std;
+using namespace dev::eth;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+class OptimizerTestFramework: public ExecutionFramework
+{
+public:
+ OptimizerTestFramework() { }
+ /// Compiles the source code with and without optimizing.
+ void compileBothVersions(
+ std::string const& _sourceCode,
+ u256 const& _value = 0,
+ std::string const& _contractName = ""
+ )
+ {
+ m_optimize = false;
+ bytes nonOptimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
+ m_nonOptimizedContract = m_contractAddress;
+ m_optimize = true;
+ bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
+ size_t nonOptimizedSize = 0;
+ eth::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
+ nonOptimizedSize++;
+ });
+ size_t optimizedSize = 0;
+ eth::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
+ optimizedSize++;
+ });
+ BOOST_CHECK_MESSAGE(
+ nonOptimizedSize > optimizedSize,
+ "Optimizer did not reduce bytecode size."
+ );
+ m_optimizedContract = m_contractAddress;
+ }
+
+ template <class... Args>
+ void compareVersions(std::string _sig, Args const&... _arguments)
+ {
+ m_contractAddress = m_nonOptimizedContract;
+ bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...);
+ m_contractAddress = m_optimizedContract;
+ bytes optimizedOutput = callContractFunction(_sig, _arguments...);
+ BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match."
+ "\nNon-Optimized: " + toHex(nonOptimizedOutput) +
+ "\nOptimized: " + toHex(optimizedOutput));
+ }
+
+ AssemblyItems addDummyLocations(AssemblyItems const& _input)
+ {
+ // add dummy locations to each item so that we can check that they are not deleted
+ AssemblyItems input = _input;
+ for (AssemblyItem& item: input)
+ item.setLocation(SourceLocation(1, 3, make_shared<string>("")));
+ return input;
+ }
+
+ eth::KnownState createInitialState(AssemblyItems const& _input)
+ {
+ eth::KnownState state;
+ for (auto const& item: addDummyLocations(_input))
+ state.feedItem(item, true);
+ return state;
+ }
+
+ AssemblyItems CSE(AssemblyItems const& _input, eth::KnownState const& _state = eth::KnownState())
+ {
+ AssemblyItems input = addDummyLocations(_input);
+
+ eth::CommonSubexpressionEliminator cse(_state);
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ AssemblyItems output = cse.getOptimizedItems();
+
+ for (AssemblyItem const& item: output)
+ {
+ BOOST_CHECK(item == Instruction::POP || !item.location().isEmpty());
+ }
+ return output;
+ }
+
+ void checkCSE(
+ AssemblyItems const& _input,
+ AssemblyItems const& _expectation,
+ KnownState const& _state = eth::KnownState()
+ )
+ {
+ AssemblyItems output = CSE(_input, _state);
+ BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
+ }
+
+ AssemblyItems CFG(AssemblyItems const& _input)
+ {
+ AssemblyItems output = _input;
+ // Running it four times should be enough for these tests.
+ for (unsigned i = 0; i < 4; ++i)
+ {
+ ControlFlowGraph cfg(output);
+ AssemblyItems optItems;
+ for (BasicBlock const& block: cfg.optimisedBlocks())
+ copy(output.begin() + block.begin, output.begin() + block.end,
+ back_inserter(optItems));
+ output = move(optItems);
+ }
+ return output;
+ }
+
+ void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
+ {
+ AssemblyItems output = CFG(_input);
+ BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
+ }
+
+protected:
+ Address m_optimizedContract;
+ Address m_nonOptimizedContract;
+};
+
+BOOST_FIXTURE_TEST_SUITE(SolidityOptimizer, OptimizerTestFramework)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a) returns (uint b) {
+ return a;
+ }
+ })";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)", u256(7));
+}
+
+BOOST_AUTO_TEST_CASE(identities)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(int a) returns (int b) {
+ return int(0) | (int(1) * (int(0) ^ (0 + a)));
+ }
+ })";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)", u256(0x12334664));
+}
+
+BOOST_AUTO_TEST_CASE(unused_expressions)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint data;
+ function f() returns (uint a, uint b) {
+ 10 + 20;
+ data;
+ }
+ })";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+}
+
+BOOST_AUTO_TEST_CASE(constant_folding_both_sides)
+{
+ // if constants involving the same associative and commutative operator are applied from both
+ // sides, the operator should be applied only once, because the expression compiler pushes
+ // literals as late as possible
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint x) returns (uint y) {
+ return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102);
+ }
+ })";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)");
+}
+
+BOOST_AUTO_TEST_CASE(storage_access)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint8[40] data;
+ function f(uint x) returns (uint y) {
+ data[2] = data[7] = uint8(x);
+ data[4] = data[2] * 10 + data[3];
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)");
+}
+
+BOOST_AUTO_TEST_CASE(array_copy)
+{
+ char const* sourceCode = R"(
+ contract test {
+ bytes2[] data1;
+ bytes5[] data2;
+ function f(uint x) returns (uint l, uint y) {
+ for (uint i = 0; i < msg.data.length; ++i)
+ data1[i] = msg.data[i];
+ data2 = data1;
+ l = data2.length;
+ y = uint(data2[x]);
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)", 0);
+ compareVersions("f(uint256)", 10);
+ compareVersions("f(uint256)", 36);
+}
+
+BOOST_AUTO_TEST_CASE(function_calls)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f1(uint x) returns (uint) { return x*x; }
+ function f(uint x) returns (uint) { return f1(7+x) - this.f1(x**9); }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)", 0);
+ compareVersions("f(uint256)", 10);
+ compareVersions("f(uint256)", 36);
+}
+
+BOOST_AUTO_TEST_CASE(storage_write_in_loops)
+{
+ char const* sourceCode = R"(
+ contract test {
+ uint d;
+ function f(uint a) returns (uint r) {
+ var x = d;
+ for (uint i = 1; i < a * a; i++) {
+ r = d;
+ d = i;
+ }
+
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256)", 0);
+ compareVersions("f(uint256)", 10);
+ compareVersions("f(uint256)", 36);
+}
+
+BOOST_AUTO_TEST_CASE(retain_information_in_branches)
+{
+ // This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches.
+ char const* sourceCode = R"(
+ contract c {
+ bytes32 d;
+ uint a;
+ function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) {
+ bytes32 z = sha3(y);
+ if (x > 8) {
+ z = sha3(y);
+ a = x;
+ } else {
+ z = sha3(y);
+ a = x;
+ }
+ r_a = a;
+ r_d = d;
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(uint256,bytes32)", 0, "abc");
+ compareVersions("f(uint256,bytes32)", 8, "def");
+ compareVersions("f(uint256,bytes32)", 10, "ghi");
+
+ m_optimize = true;
+ bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
+ size_t numSHA3s = 0;
+ eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
+ if (_instr == eth::Instruction::SHA3)
+ numSHA3s++;
+ });
+ BOOST_CHECK_EQUAL(1, numSHA3s);
+}
+
+BOOST_AUTO_TEST_CASE(store_tags_as_unions)
+{
+ // This calls the same function from two sources and both calls have a certain sha3 on
+ // the stack at the same position.
+ // Without storing tags as unions, the return from the shared function would not know where to
+ // jump and thus all jumpdests are forced to clear their state and we do not know about the
+ // sha3 anymore.
+ // Note that, for now, this only works if the functions have the same number of return
+ // parameters since otherwise, the return jump addresses are at different stack positions
+ // which triggers the "unknown jump target" situation.
+ char const* sourceCode = R"(
+ contract test {
+ bytes32 data;
+ function f(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) {
+ r_d = sha3(y);
+ shared(y);
+ r_d = sha3(y);
+ r_a = 5;
+ }
+ function g(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) {
+ r_d = sha3(y);
+ shared(y);
+ r_d = bytes32(uint(sha3(y)) + 2);
+ r_a = 7;
+ }
+ function shared(bytes32 y) internal {
+ data = sha3(y);
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()", 7, "abc");
+
+ m_optimize = true;
+ bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test");
+ size_t numSHA3s = 0;
+ eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
+ if (_instr == eth::Instruction::SHA3)
+ numSHA3s++;
+ });
+// TEST DISABLED UNTIL 93693404 IS IMPLEMENTED
+// BOOST_CHECK_EQUAL(2, numSHA3s);
+}
+
+BOOST_AUTO_TEST_CASE(successor_not_found_bug)
+{
+ // This bug was caused because MSVC chose to use the u256->bool conversion
+ // instead of u256->unsigned
+ char const* sourceCode = R"(
+ contract greeter { function greeter() {} }
+ )";
+ compileBothVersions(sourceCode);
+}
+
+BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug)
+{
+ // This bug appeared because a sha3 operation with too low sequence number was used,
+ // resulting in memory not being rewritten before the sha3. The fix was to
+ // take the max of the min sequence numbers when merging the states.
+ char const* sourceCode = R"(
+ contract C
+ {
+ mapping(uint => uint) data;
+ function f() returns (uint)
+ {
+ if(data[now] == 0)
+ data[uint(-7)] = 5;
+ return data[now];
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+}
+
+BOOST_AUTO_TEST_CASE(sequence_number_for_calls)
+{
+ // This is a test for a bug that was present because we did not increment the sequence
+ // number for CALLs - CALLs can read and write from memory (and DELEGATECALLs can do the same
+ // to storage), so the sequence number should be incremented.
+ char const* sourceCode = R"(
+ contract test {
+ function f(string a, string b) returns (bool) { return sha256(a) == sha256(b); }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f(string,string)", 0x40, 0x80, 3, "abc", 3, "def");
+}
+
+BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
+{
+ eth::KnownState state;
+ eth::CommonSubexpressionEliminator cse(state);
+ AssemblyItems input{
+ Instruction::SWAP1, Instruction::POP, Instruction::ADD, u256(0), Instruction::SWAP1,
+ Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1,
+ Instruction::DIV, u256(0xff), Instruction::AND
+ };
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ AssemblyItems output = cse.getOptimizedItems();
+ BOOST_CHECK(!output.empty());
+}
+
+BOOST_AUTO_TEST_CASE(cse_negative_stack_access)
+{
+ AssemblyItems input{Instruction::DUP2, u256(0)};
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_negative_stack_end)
+{
+ AssemblyItems input{Instruction::ADD};
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_intermediate_negative_stack)
+{
+ AssemblyItems input{Instruction::ADD, u256(1), Instruction::DUP1};
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_pop)
+{
+ checkCSE({Instruction::POP}, {Instruction::POP});
+}
+
+BOOST_AUTO_TEST_CASE(cse_unneeded_items)
+{
+ AssemblyItems input{
+ Instruction::ADD,
+ Instruction::SWAP1,
+ Instruction::POP,
+ u256(7),
+ u256(8),
+ };
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_constant_addition)
+{
+ AssemblyItems input{u256(7), u256(8), Instruction::ADD};
+ checkCSE(input, {u256(7 + 8)});
+}
+
+BOOST_AUTO_TEST_CASE(cse_invariants)
+{
+ AssemblyItems input{
+ Instruction::DUP1,
+ Instruction::DUP1,
+ u256(0),
+ Instruction::OR,
+ Instruction::OR
+ };
+ checkCSE(input, {Instruction::DUP1});
+}
+
+BOOST_AUTO_TEST_CASE(cse_subself)
+{
+ checkCSE({Instruction::DUP1, Instruction::SUB}, {Instruction::POP, u256(0)});
+}
+
+BOOST_AUTO_TEST_CASE(cse_subother)
+{
+ checkCSE({Instruction::SUB}, {Instruction::SUB});
+}
+
+BOOST_AUTO_TEST_CASE(cse_double_negation)
+{
+ checkCSE({Instruction::DUP5, Instruction::NOT, Instruction::NOT}, {Instruction::DUP5});
+}
+
+BOOST_AUTO_TEST_CASE(cse_double_iszero)
+{
+ checkCSE({Instruction::GT, Instruction::ISZERO, Instruction::ISZERO}, {Instruction::GT});
+ checkCSE({Instruction::GT, Instruction::ISZERO}, {Instruction::GT, Instruction::ISZERO});
+ checkCSE(
+ {Instruction::ISZERO, Instruction::ISZERO, Instruction::ISZERO},
+ {Instruction::ISZERO}
+ );
+}
+
+BOOST_AUTO_TEST_CASE(cse_associativity)
+{
+ AssemblyItems input{
+ Instruction::DUP1,
+ Instruction::DUP1,
+ u256(0),
+ Instruction::OR,
+ Instruction::OR
+ };
+ checkCSE(input, {Instruction::DUP1});
+}
+
+BOOST_AUTO_TEST_CASE(cse_associativity2)
+{
+ AssemblyItems input{
+ u256(0),
+ Instruction::DUP2,
+ u256(2),
+ u256(1),
+ Instruction::DUP6,
+ Instruction::ADD,
+ u256(2),
+ Instruction::ADD,
+ Instruction::ADD,
+ Instruction::ADD,
+ Instruction::ADD
+ };
+ checkCSE(input, {Instruction::DUP2, Instruction::DUP2, Instruction::ADD, u256(5), Instruction::ADD});
+}
+
+BOOST_AUTO_TEST_CASE(cse_storage)
+{
+ AssemblyItems input{
+ u256(0),
+ Instruction::SLOAD,
+ u256(0),
+ Instruction::SLOAD,
+ Instruction::ADD,
+ u256(0),
+ Instruction::SSTORE
+ };
+ checkCSE(input, {
+ u256(0),
+ Instruction::DUP1,
+ Instruction::SLOAD,
+ Instruction::DUP1,
+ Instruction::ADD,
+ Instruction::SWAP1,
+ Instruction::SSTORE
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_noninterleaved_storage)
+{
+ // two stores to the same location should be replaced by only one store, even if we
+ // read in the meantime
+ AssemblyItems input{
+ u256(7),
+ Instruction::DUP2,
+ Instruction::SSTORE,
+ Instruction::DUP1,
+ Instruction::SLOAD,
+ u256(8),
+ Instruction::DUP3,
+ Instruction::SSTORE
+ };
+ checkCSE(input, {
+ u256(8),
+ Instruction::DUP2,
+ Instruction::SSTORE,
+ u256(7)
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_interleaved_storage)
+{
+ // stores and reads to/from two unknown locations, should not optimize away the first store
+ AssemblyItems input{
+ u256(7),
+ Instruction::DUP2,
+ Instruction::SSTORE, // store to "DUP1"
+ Instruction::DUP2,
+ Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
+ u256(0),
+ Instruction::DUP3,
+ Instruction::SSTORE // store different value to "DUP1"
+ };
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_interleaved_storage_same_value)
+{
+ // stores and reads to/from two unknown locations, should not optimize away the first store
+ // but it should optimize away the second, since we already know the value will be the same
+ AssemblyItems input{
+ u256(7),
+ Instruction::DUP2,
+ Instruction::SSTORE, // store to "DUP1"
+ Instruction::DUP2,
+ Instruction::SLOAD, // read from "DUP2", might be equal to "DUP1"
+ u256(6),
+ u256(1),
+ Instruction::ADD,
+ Instruction::DUP3,
+ Instruction::SSTORE // store same value to "DUP1"
+ };
+ checkCSE(input, {
+ u256(7),
+ Instruction::DUP2,
+ Instruction::SSTORE,
+ Instruction::DUP2,
+ Instruction::SLOAD
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location)
+{
+ // stores and reads to/from two known locations, should optimize away the first store,
+ // because we know that the location is different
+ AssemblyItems input{
+ u256(0x70),
+ u256(1),
+ Instruction::SSTORE, // store to 1
+ u256(2),
+ Instruction::SLOAD, // read from 2, is different from 1
+ u256(0x90),
+ u256(1),
+ Instruction::SSTORE // store different value at 1
+ };
+ checkCSE(input, {
+ u256(2),
+ Instruction::SLOAD,
+ u256(0x90),
+ u256(1),
+ Instruction::SSTORE
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset)
+{
+ // stores and reads to/from two locations which are known to be different,
+ // should optimize away the first store, because we know that the location is different
+ AssemblyItems input{
+ u256(0x70),
+ Instruction::DUP2,
+ u256(1),
+ Instruction::ADD,
+ Instruction::SSTORE, // store to "DUP1"+1
+ Instruction::DUP1,
+ u256(2),
+ Instruction::ADD,
+ Instruction::SLOAD, // read from "DUP1"+2, is different from "DUP1"+1
+ u256(0x90),
+ Instruction::DUP3,
+ u256(1),
+ Instruction::ADD,
+ Instruction::SSTORE // store different value at "DUP1"+1
+ };
+ checkCSE(input, {
+ u256(2),
+ Instruction::DUP2,
+ Instruction::ADD,
+ Instruction::SLOAD,
+ u256(0x90),
+ u256(1),
+ Instruction::DUP4,
+ Instruction::ADD,
+ Instruction::SSTORE
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset)
+{
+ // stores and reads to/from two locations which are known to be different,
+ // should not optimize away the first store, because the location overlaps with the load,
+ // but it should optimize away the second, because we know that the location is different by 32
+ AssemblyItems input{
+ u256(0x50),
+ Instruction::DUP2,
+ u256(2),
+ Instruction::ADD,
+ Instruction::MSTORE, // ["DUP1"+2] = 0x50
+ u256(0x60),
+ Instruction::DUP2,
+ u256(32),
+ Instruction::ADD,
+ Instruction::MSTORE, // ["DUP1"+32] = 0x60
+ Instruction::DUP1,
+ Instruction::MLOAD, // read from "DUP1"
+ u256(0x70),
+ Instruction::DUP3,
+ u256(32),
+ Instruction::ADD,
+ Instruction::MSTORE, // ["DUP1"+32] = 0x70
+ u256(0x80),
+ Instruction::DUP3,
+ u256(2),
+ Instruction::ADD,
+ Instruction::MSTORE, // ["DUP1"+2] = 0x80
+ };
+ // If the actual code changes too much, we could also simply check that the output contains
+ // exactly 3 MSTORE and exactly 1 MLOAD instruction.
+ checkCSE(input, {
+ u256(0x50),
+ u256(2),
+ Instruction::DUP3,
+ Instruction::ADD,
+ Instruction::SWAP1,
+ Instruction::DUP2,
+ Instruction::MSTORE, // ["DUP1"+2] = 0x50
+ Instruction::DUP2,
+ Instruction::MLOAD, // read from "DUP1"
+ u256(0x70),
+ u256(32),
+ Instruction::DUP5,
+ Instruction::ADD,
+ Instruction::MSTORE, // ["DUP1"+32] = 0x70
+ u256(0x80),
+ Instruction::SWAP1,
+ Instruction::SWAP2,
+ Instruction::MSTORE // ["DUP1"+2] = 0x80
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_deep_stack)
+{
+ AssemblyItems input{
+ Instruction::ADD,
+ Instruction::SWAP1,
+ Instruction::POP,
+ Instruction::SWAP8,
+ Instruction::POP,
+ Instruction::SWAP8,
+ Instruction::POP,
+ Instruction::SWAP8,
+ Instruction::SWAP5,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ };
+ checkCSE(input, {
+ Instruction::SWAP4,
+ Instruction::SWAP12,
+ Instruction::SWAP3,
+ Instruction::SWAP11,
+ Instruction::POP,
+ Instruction::SWAP1,
+ Instruction::SWAP3,
+ Instruction::ADD,
+ Instruction::SWAP8,
+ Instruction::POP,
+ Instruction::SWAP6,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ Instruction::POP,
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump)
+{
+ AssemblyItems input{
+ u256(0),
+ u256(1),
+ Instruction::DUP2,
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMPI
+ };
+ checkCSE(input, {
+ u256(0),
+ u256(1)
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_jumpi_jump)
+{
+ AssemblyItems input{
+ u256(1),
+ u256(1),
+ Instruction::DUP2,
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMPI
+ };
+ checkCSE(input, {
+ u256(1),
+ Instruction::DUP1,
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMP
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_empty_sha3)
+{
+ AssemblyItems input{
+ u256(0),
+ Instruction::DUP2,
+ Instruction::SHA3
+ };
+ checkCSE(input, {
+ u256(sha3(bytesConstRef()))
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_partial_sha3)
+{
+ AssemblyItems input{
+ u256(0xabcd) << (256 - 16),
+ u256(0),
+ Instruction::MSTORE,
+ u256(2),
+ u256(0),
+ Instruction::SHA3
+ };
+ checkCSE(input, {
+ u256(0xabcd) << (256 - 16),
+ u256(0),
+ Instruction::MSTORE,
+ u256(sha3(bytes{0xab, 0xcd}))
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location)
+{
+ // sha3 twice from same dynamic location
+ AssemblyItems input{
+ Instruction::DUP2,
+ Instruction::DUP1,
+ Instruction::MSTORE,
+ u256(64),
+ Instruction::DUP2,
+ Instruction::SHA3,
+ u256(64),
+ Instruction::DUP3,
+ Instruction::SHA3
+ };
+ checkCSE(input, {
+ Instruction::DUP2,
+ Instruction::DUP1,
+ Instruction::MSTORE,
+ u256(64),
+ Instruction::DUP2,
+ Instruction::SHA3,
+ Instruction::DUP1
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content)
+{
+ // sha3 twice from different dynamic location but with same content
+ AssemblyItems input{
+ Instruction::DUP1,
+ u256(0x80),
+ Instruction::MSTORE, // m[128] = DUP1
+ u256(0x20),
+ u256(0x80),
+ Instruction::SHA3, // sha3(m[128..(128+32)])
+ Instruction::DUP2,
+ u256(12),
+ Instruction::MSTORE, // m[12] = DUP1
+ u256(0x20),
+ u256(12),
+ Instruction::SHA3 // sha3(m[12..(12+32)])
+ };
+ checkCSE(input, {
+ u256(0x80),
+ Instruction::DUP2,
+ Instruction::DUP2,
+ Instruction::MSTORE,
+ u256(0x20),
+ Instruction::SWAP1,
+ Instruction::SHA3,
+ u256(12),
+ Instruction::DUP3,
+ Instruction::SWAP1,
+ Instruction::MSTORE,
+ Instruction::DUP1
+ });
+}
+
+BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between)
+{
+ // sha3 twice from different dynamic location but with same content,
+ // dynamic mstore in between, which forces us to re-calculate the sha3
+ AssemblyItems input{
+ u256(0x80),
+ Instruction::DUP2,
+ Instruction::DUP2,
+ Instruction::MSTORE, // m[128] = DUP1
+ u256(0x20),
+ Instruction::DUP1,
+ Instruction::DUP3,
+ Instruction::SHA3, // sha3(m[128..(128+32)])
+ u256(12),
+ Instruction::DUP5,
+ Instruction::DUP2,
+ Instruction::MSTORE, // m[12] = DUP1
+ Instruction::DUP12,
+ Instruction::DUP14,
+ Instruction::MSTORE, // destroys memory knowledge
+ Instruction::SWAP2,
+ Instruction::SWAP1,
+ Instruction::SWAP2,
+ Instruction::SHA3 // sha3(m[12..(12+32)])
+ };
+ checkCSE(input, input);
+}
+
+BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between)
+{
+ // sha3 twice from different dynamic location but with same content,
+ // dynamic mstore in between, but does not force us to re-calculate the sha3
+ AssemblyItems input{
+ u256(0x80),
+ Instruction::DUP2,
+ Instruction::DUP2,
+ Instruction::MSTORE, // m[128] = DUP1
+ u256(0x20),
+ Instruction::DUP1,
+ Instruction::DUP3,
+ Instruction::SHA3, // sha3(m[128..(128+32)])
+ u256(12),
+ Instruction::DUP5,
+ Instruction::DUP2,
+ Instruction::MSTORE, // m[12] = DUP1
+ Instruction::DUP12,
+ u256(12 + 32),
+ Instruction::MSTORE, // does not destoy memory knowledge
+ Instruction::DUP13,
+ u256(128 - 32),
+ Instruction::MSTORE, // does not destoy memory knowledge
+ u256(0x20),
+ u256(12),
+ Instruction::SHA3 // sha3(m[12..(12+32)])
+ };
+ // if this changes too often, only count the number of SHA3 and MSTORE instructions
+ AssemblyItems output = CSE(input);
+ BOOST_CHECK_EQUAL(4, count(output.begin(), output.end(), AssemblyItem(Instruction::MSTORE)));
+ BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3)));
+}
+
+BOOST_AUTO_TEST_CASE(cse_with_initially_known_stack)
+{
+ eth::KnownState state = createInitialState(AssemblyItems{
+ u256(0x12),
+ u256(0x20),
+ Instruction::ADD
+ });
+ AssemblyItems input{
+ u256(0x12 + 0x20)
+ };
+ checkCSE(input, AssemblyItems{Instruction::DUP1}, state);
+}
+
+BOOST_AUTO_TEST_CASE(cse_equality_on_initially_known_stack)
+{
+ eth::KnownState state = createInitialState(AssemblyItems{Instruction::DUP1});
+ AssemblyItems input{
+ Instruction::EQ
+ };
+ AssemblyItems output = CSE(input, state);
+ // check that it directly pushes 1 (true)
+ BOOST_CHECK(find(output.begin(), output.end(), AssemblyItem(u256(1))) != output.end());
+}
+
+BOOST_AUTO_TEST_CASE(cse_access_previous_sequence)
+{
+ // Tests that the code generator detects whether it tries to access SLOAD instructions
+ // from a sequenced expression which is not in its scope.
+ eth::KnownState state = createInitialState(AssemblyItems{
+ u256(0),
+ Instruction::SLOAD,
+ u256(1),
+ Instruction::ADD,
+ u256(0),
+ Instruction::SSTORE
+ });
+ // now stored: val_1 + 1 (value at sequence 1)
+ // if in the following instructions, the SLOAD cresolves to "val_1 + 1",
+ // this cannot be generated because we cannot load from sequence 1 anymore.
+ AssemblyItems input{
+ u256(0),
+ Instruction::SLOAD,
+ };
+ BOOST_CHECK_THROW(CSE(input, state), StackTooDeepException);
+ // @todo for now, this throws an exception, but it should recover to the following
+ // (or an even better version) at some point:
+ // 0, SLOAD, 1, ADD, SSTORE, 0 SLOAD
+}
+
+BOOST_AUTO_TEST_CASE(cse_optimise_return)
+{
+ checkCSE(
+ AssemblyItems{u256(0), u256(7), Instruction::RETURN},
+ AssemblyItems{Instruction::STOP}
+ );
+}
+
+BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused)
+{
+ // remove parts of the code that are unused
+ AssemblyItems input{
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMP,
+ u256(7),
+ AssemblyItem(Tag, 1),
+ };
+ checkCFG(input, {});
+}
+
+BOOST_AUTO_TEST_CASE(control_flow_graph_remove_unused_loop)
+{
+ AssemblyItems input{
+ AssemblyItem(PushTag, 3),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 1),
+ u256(7),
+ AssemblyItem(PushTag, 2),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 2),
+ u256(8),
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 3),
+ u256(11)
+ };
+ checkCFG(input, {u256(11)});
+}
+
+BOOST_AUTO_TEST_CASE(control_flow_graph_reconnect_single_jump_source)
+{
+ // move code that has only one unconditional jump source
+ AssemblyItems input{
+ u256(1),
+ AssemblyItem(PushTag, 1),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 2),
+ u256(2),
+ AssemblyItem(PushTag, 3),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 1),
+ u256(3),
+ AssemblyItem(PushTag, 2),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 3),
+ u256(4),
+ };
+ checkCFG(input, {u256(1), u256(3), u256(2), u256(4)});
+}
+
+BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to)
+{
+ // do not remove parts that are "returned to"
+ AssemblyItems input{
+ AssemblyItem(PushTag, 1),
+ AssemblyItem(PushTag, 2),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 2),
+ Instruction::JUMP,
+ AssemblyItem(Tag, 1),
+ u256(2)
+ };
+ checkCFG(input, {u256(2)});
+}
+
+BOOST_AUTO_TEST_CASE(block_deduplicator)
+{
+ AssemblyItems input{
+ AssemblyItem(PushTag, 2),
+ AssemblyItem(PushTag, 1),
+ AssemblyItem(PushTag, 3),
+ u256(6),
+ eth::Instruction::SWAP3,
+ eth::Instruction::JUMP,
+ AssemblyItem(Tag, 1),
+ u256(6),
+ eth::Instruction::SWAP3,
+ eth::Instruction::JUMP,
+ AssemblyItem(Tag, 2),
+ u256(6),
+ eth::Instruction::SWAP3,
+ eth::Instruction::JUMP,
+ AssemblyItem(Tag, 3)
+ };
+ BlockDeduplicator dedup(input);
+ dedup.deduplicate();
+
+ set<u256> pushTags;
+ for (AssemblyItem const& item: input)
+ if (item.type() == PushTag)
+ pushTags.insert(item.data());
+ BOOST_CHECK_EQUAL(pushTags.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
+{
+ AssemblyItems input{
+ u256(0),
+ eth::Instruction::SLOAD,
+ AssemblyItem(PushTag, 1),
+ AssemblyItem(PushTag, 2),
+ eth::Instruction::JUMPI,
+ eth::Instruction::JUMP,
+ AssemblyItem(Tag, 1),
+ u256(5),
+ u256(6),
+ eth::Instruction::SSTORE,
+ AssemblyItem(PushTag, 1),
+ eth::Instruction::JUMP,
+ AssemblyItem(Tag, 2),
+ u256(5),
+ u256(6),
+ eth::Instruction::SSTORE,
+ AssemblyItem(PushTag, 2),
+ eth::Instruction::JUMP,
+ };
+ BlockDeduplicator dedup(input);
+ dedup.deduplicate();
+
+ set<u256> pushTags;
+ for (AssemblyItem const& item: input)
+ if (item.type() == PushTag)
+ pushTags.insert(item.data());
+ BOOST_CHECK_EQUAL(pushTags.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(computing_constants)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint a;
+ uint b;
+ uint c;
+ function set() returns (uint a, uint b, uint c) {
+ a = 0x77abc0000000000000000000000000000000000000000000000000000000001;
+ b = 0x817416927846239487123469187231298734162934871263941234127518276;
+ g();
+ }
+ function g() {
+ b = 0x817416927846239487123469187231298734162934871263941234127518276;
+ c = 0x817416927846239487123469187231298734162934871263941234127518276;
+ }
+ function get() returns (uint ra, uint rb, uint rc) {
+ ra = a;
+ rb = b;
+ rc = c ;
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("set()");
+ compareVersions("get()");
+
+ m_optimize = true;
+ m_optimizeRuns = 1;
+ bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
+ bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276"));
+ unsigned occurrences = 0;
+ for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences)
+ {
+ iter = search(iter, optimizedBytecode.cend(), complicatedConstant.cbegin(), complicatedConstant.cend());
+ if (iter < optimizedBytecode.cend())
+ ++iter;
+ }
+ BOOST_CHECK_EQUAL(2, occurrences);
+
+ bytes constantWithZeros = toBigEndian(u256("0x77abc0000000000000000000000000000000000000000000000000000000001"));
+ BOOST_CHECK(search(
+ optimizedBytecode.cbegin(),
+ optimizedBytecode.cend(),
+ constantWithZeros.cbegin(),
+ constantWithZeros.cend()
+ ) == optimizedBytecode.cend());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
new file mode 100644
index 00000000..2a7e82a7
--- /dev/null
+++ b/test/libsolidity/SolidityParser.cpp
@@ -0,0 +1,1185 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity parser.
+ */
+
+#include <string>
+#include <memory>
+#include <libdevcore/Log.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/interface/Exceptions.h>
+#include "../TestHelper.h"
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors)
+{
+ ASTPointer<SourceUnit> sourceUnit = Parser(_errors).parse(std::make_shared<Scanner>(CharStream(_source)));
+ if (!sourceUnit)
+ return ASTPointer<ContractDefinition>();
+ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
+ if (ASTPointer<ContractDefinition> contract = dynamic_pointer_cast<ContractDefinition>(node))
+ return contract;
+ BOOST_FAIL("No contract found in source.");
+ return ASTPointer<ContractDefinition>();
+}
+
+bool successParse(std::string const& _source)
+{
+ ErrorList errors;
+ try
+ {
+ auto sourceUnit = parseText(_source, errors);
+ if (!sourceUnit)
+ return false;
+ }
+ catch (FatalError const& /*_exception*/)
+ {
+ if (Error::containsErrorOfType(errors, Error::Type::ParserError))
+ return false;
+ }
+ if (Error::containsErrorOfType(errors, Error::Type::ParserError))
+ return false;
+
+ BOOST_CHECK(Error::containsOnlyWarnings(errors));
+ return true;
+}
+
+void checkFunctionNatspec(
+ FunctionDefinition const* _function,
+ std::string const& _expectedDoc
+)
+{
+ auto doc = _function->documentation();
+ BOOST_CHECK_MESSAGE(doc != nullptr, "Function does not have Natspec Doc as expected");
+ BOOST_CHECK_EQUAL(*doc, _expectedDoc);
+}
+
+}
+
+
+BOOST_AUTO_TEST_SUITE(SolidityParser)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVariable1;\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration)
+{
+ char const* text = "contract test {\n"
+ " uint256 ;\n"
+ "}\n";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_function)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " function functionName(bytes20 arg1, address addr) constant\n"
+ " returns (int id)\n"
+ " { }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(no_function_params)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " function functionName() {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(single_function_param)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " function functionName(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_no_body)
+{
+ char const* text = "contract test {\n"
+ " function functionName(bytes32 input) returns (bytes32 out);\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
+{
+ char const* text = "contract test {\n"
+ " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
+ " function b() returns (uint r) { r = a({: 1, : 2, : 3}); }\n"
+ "}\n";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(missing_argument_in_named_args)
+{
+ char const* text = "contract test {\n"
+ " function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }\n"
+ " function b() returns (uint r) { r = a({a: , b: , c: }); }\n"
+ "}\n";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(two_exact_functions)
+{
+ char const* text = R"(
+ contract test {
+ function fun(uint a) returns(uint r) { return a; }
+ function fun(uint a) returns(uint r) { return a; }
+ }
+ )";
+ // with support of overloaded functions, during parsing,
+ // we can't determine whether they match exactly, however
+ // it will throw DeclarationError in following stage.
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(overloaded_functions)
+{
+ char const* text = R"(
+ contract test {
+ function fun(uint a) returns(uint r) { return a; }
+ function fun(uint a, uint b) returns(uint r) { return a + b; }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(function_natspec_documentation)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " /// This is a test function\n"
+ " function functionName(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ FunctionDefinition const* function = nullptr;
+ auto functions = contract->definedFunctions();
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is a test function");
+}
+
+BOOST_AUTO_TEST_CASE(function_normal_comments)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " // We won't see this comment\n"
+ " function functionName(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ BOOST_CHECK_MESSAGE(function->documentation() == nullptr,
+ "Should not have gotten a Natspecc comment for this function");
+}
+
+BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " /// This is test function 1\n"
+ " function functionName1(bytes32 input) returns (bytes32 out) {}\n"
+ " /// This is test function 2\n"
+ " function functionName2(bytes32 input) returns (bytes32 out) {}\n"
+ " // nothing to see here\n"
+ " function functionName3(bytes32 input) returns (bytes32 out) {}\n"
+ " /// This is test function 4\n"
+ " function functionName4(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is test function 1");
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(1), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is test function 2");
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(2), "Failed to retrieve function");
+ BOOST_CHECK_MESSAGE(function->documentation() == nullptr,
+ "Should not have gotten natspec comment for functionName3()");
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(3), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is test function 4");
+}
+
+BOOST_AUTO_TEST_CASE(multiline_function_documentation)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " /// This is a test function\n"
+ " /// and it has 2 lines\n"
+ " function functionName1(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is a test function\n"
+ " and it has 2 lines");
+}
+
+BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " /// fun1 description\n"
+ " function fun1(uint256 a) {\n"
+ " var b;\n"
+ " /// I should not interfere with actual natspec comments\n"
+ " uint256 c;\n"
+ " mapping(address=>bytes32) d;\n"
+ " bytes7 name = \"Solidity\";"
+ " }\n"
+ " /// This is a test function\n"
+ " /// and it has 2 lines\n"
+ " function fun(bytes32 input) returns (bytes32 out) {}\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ checkFunctionNatspec(function, "fun1 description");
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(1), "Failed to retrieve function");
+ checkFunctionNatspec(function, "This is a test function\n"
+ " and it has 2 lines");
+}
+
+BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " function ///I am in the wrong place \n"
+ " fun1(uint256 a) {\n"
+ " var b;\n"
+ " /// I should not interfere with actual natspec comments\n"
+ " uint256 c;\n"
+ " mapping(address=>bytes32) d;\n"
+ " bytes7 name = \"Solidity\";"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ BOOST_CHECK_MESSAGE(!function->documentation(),
+ "Shouldn't get natspec docstring for this function");
+}
+
+BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
+{
+ FunctionDefinition const* function = nullptr;
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " function fun1(uint256 a) {\n"
+ " /// I should have been above the function signature\n"
+ " var b;\n"
+ " /// I should not interfere with actual natspec comments\n"
+ " uint256 c;\n"
+ " mapping(address=>bytes32) d;\n"
+ " bytes7 name = \"Solidity\";"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+ ErrorList errors;
+ ASTPointer<ContractDefinition> contract = parseText(text, errors);
+ auto functions = contract->definedFunctions();
+
+ ETH_TEST_REQUIRE_NO_THROW(function = functions.at(0), "Failed to retrieve function");
+ BOOST_CHECK_MESSAGE(!function->documentation(),
+ "Shouldn't get natspec docstring for this function");
+}
+
+BOOST_AUTO_TEST_CASE(struct_definition)
+{
+ char const* text = "contract test {\n"
+ " uint256 stateVar;\n"
+ " struct MyStructName {\n"
+ " address addr;\n"
+ " uint256 count;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(mapping)
+{
+ char const* text = "contract test {\n"
+ " mapping(address => bytes32) names;\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_in_struct)
+{
+ char const* text = "contract test {\n"
+ " struct test_struct {\n"
+ " address addr;\n"
+ " uint256 count;\n"
+ " mapping(bytes32 => test_struct) self_reference;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(mapping_to_mapping_in_struct)
+{
+ char const* text = "contract test {\n"
+ " struct test_struct {\n"
+ " address addr;\n"
+ " mapping (uint64 => mapping (bytes32 => uint)) complex_mapping;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(variable_definition)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " var b;\n"
+ " uint256 c;\n"
+ " mapping(address=>bytes32) d;\n"
+ " customtype varname;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(variable_definition_with_initialization)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " var b = 2;\n"
+ " uint256 c = 0x87;\n"
+ " mapping(address=>bytes32) d;\n"
+ " bytes7 name = \"Solidity\";"
+ " customtype varname;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(variable_definition_in_function_parameter)
+{
+ char const* text = R"(
+ contract test {
+ function fun(var a) {}
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(variable_definition_in_mapping)
+{
+ char const* text = R"(
+ contract test {
+ function fun() {
+ mapping(var=>bytes32) d;
+ }
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(variable_definition_in_function_return)
+{
+ char const* text = R"(
+ contract test {
+ function fun() returns(var d) {
+ return 1;
+ }
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(operator_expression)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " uint256 x = (1 + 4) || false && (1 - 12) + -9;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(complex_expression)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " uint256 x = (1 + 4).member(++67)[a/=9] || true;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(exp_expression)
+{
+ char const* text = R"(
+ contract test {
+ function fun(uint256 a) {
+ uint256 x = 3 ** a;
+ }
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(while_loop)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " while (true) { uint256 x = 1; break; continue; } x = 9;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_vardef_initexpr)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " for (uint256 i = 0; i < 10; i++)\n"
+ " { uint256 x = i; break; continue; }\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " uint256 i =0;\n"
+ " for (i = 0; i < 10; i++)\n"
+ " { uint256 x = i; break; continue; }\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " uint256 i =0;\n"
+ " for (;;)\n"
+ " { uint256 x = i; break; continue; }\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(for_loop_single_stmt_body)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " uint256 i =0;\n"
+ " for (i = 0; i < 10; i++)\n"
+ " continue;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(if_statement)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) {\n"
+ " if (a >= 8) return 2; else { var b = 7; }\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(else_if_statement)
+{
+ char const* text = "contract test {\n"
+ " function fun(uint256 a) returns (address b) {\n"
+ " if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78;\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " uint64[7](3);\n"
+ " uint64[](3);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(type_conversion_to_dynamic_array)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " var x = uint64[](3);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(import_directive)
+{
+ char const* text = "import \"abc\";\n"
+ "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(multiple_contracts)
+{
+ char const* text = "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "contract test2 {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)
+{
+ char const* text = "import \"abc\";\n"
+ "contract test {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "import \"def\";\n"
+ "contract test2 {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "import \"ghi\";\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(contract_inheritance)
+{
+ char const* text = "contract base {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "contract derived is base {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(contract_multiple_inheritance)
+{
+ char const* text = "contract base {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "contract derived is base, nonExisting {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments)
+{
+ char const* text = "contract base {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n"
+ "contract derived is base(2), nonExisting(\"abc\", \"def\", base.fun()) {\n"
+ " function fun() {\n"
+ " uint64(2);\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(placeholder_in_function_context)
+{
+ char const* text = "contract c {\n"
+ " function fun() returns (uint r) {\n"
+ " var _ = 8;\n"
+ " return _ + 1;"
+ " }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(modifier)
+{
+ char const* text = "contract c {\n"
+ " modifier mod { if (msg.sender == 0) _ }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(modifier_arguments)
+{
+ char const* text = "contract c {\n"
+ " modifier mod(uint a) { if (msg.sender == a) _ }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(modifier_invocation)
+{
+ char const* text = "contract c {\n"
+ " modifier mod1(uint a) { if (msg.sender == a) _ }\n"
+ " modifier mod2 { if (msg.sender == 2) _ }\n"
+ " function f() mod1(7) mod2 { }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(fallback_function)
+{
+ char const* text = "contract c {\n"
+ " function() { }\n"
+ "}\n";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(event)
+{
+ char const* text = R"(
+ contract c {
+ event e();
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(event_arguments)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint a, bytes32 s);
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(event_arguments_indexed)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint a, bytes32 indexed s, bool indexed b);
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(visibility_specifiers)
+{
+ char const* text = R"(
+ contract c {
+ uint private a;
+ uint internal b;
+ uint public c;
+ uint d;
+ function f() {}
+ function f_priv() private {}
+ function f_public() public {}
+ function f_internal() internal {}
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers)
+{
+ char const* text = R"(
+ contract c {
+ uint private internal a;
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations)
+{
+ char const* text = R"(
+ contract c {
+ function c ()
+ {
+ a = 1 wei;
+ b = 2 szabo;
+ c = 3 finney;
+ b = 4 ether;
+ }
+ uint256 a;
+ uint256 b;
+ uint256 c;
+ uint256 d;
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations_in_expressions)
+{
+ char const* text = R"(
+ contract c {
+ function c ()
+ {
+ a = 1 wei * 100 wei + 7 szabo - 3;
+ }
+ uint256 a;
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(enum_valid_declaration)
+{
+ char const* text = R"(
+ contract c {
+ enum validEnum { Value1, Value2, Value3, Value4 }
+ function c ()
+ {
+ a = foo.Value3;
+ }
+ uint256 a;
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_enum_declaration)
+{
+ char const* text = R"(
+ contract c {
+ enum foo { }
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(malformed_enum_declaration)
+{
+ char const* text = R"(
+ contract c {
+ enum foo { WARNING,}
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(external_function)
+{
+ char const* text = R"(
+ contract c {
+ function x() external {}
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(external_variable)
+{
+ char const* text = R"(
+ contract c {
+ uint external x;
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_in_storage)
+{
+ char const* text = R"(
+ contract c {
+ uint[10] a;
+ uint[] a2;
+ struct x { uint[2**20] b; y[0] c; }
+ struct y { uint d; mapping(uint=>x)[] e; }
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_in_events)
+{
+ char const* text = R"(
+ contract c {
+ event e(uint[10] a, bytes7[8] indexed b, c[3] x);
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(arrays_in_expressions)
+{
+ char const* text = R"(
+ contract c {
+ function f() { c[10] a = 7; uint8[10 * 2] x; }
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(multi_arrays)
+{
+ char const* text = R"(
+ contract c {
+ mapping(uint => mapping(uint => int8)[8][][9])[] x;
+ })";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(constant_is_keyword)
+{
+ char const* text = R"(
+ contract Foo {
+ uint constant = 4;
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(var_array)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() { var[] a; }
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(location_specifiers_for_params)
+{
+ char const* text = R"(
+ contract Foo {
+ function f(uint[] storage constant x, uint[] memory y) { }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(location_specifiers_for_locals)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() {
+ uint[] storage x;
+ uint[] memory y;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(location_specifiers_for_state)
+{
+ char const* text = R"(
+ contract Foo {
+ uint[] memory x;
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(location_specifiers_with_var)
+{
+ char const* text = R"(
+ contract Foo {
+ function f() { var memory x; }
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(empty_comment)
+{
+ char const* text = R"(
+ //
+ contract test
+ {}
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(library_simple)
+{
+ char const* text = R"(
+ library Lib {
+ function f() { }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+
+BOOST_AUTO_TEST_CASE(local_const_variable)
+{
+ char const* text = R"(
+ contract Foo {
+ function localConst() returns (uint ret)
+ {
+ uint constant local = 4;
+ return local;
+ }
+ })";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(multi_variable_declaration)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ var (a,b,c) = g();
+ var (d) = 2;
+ var (,e) = 3;
+ var (f,) = 4;
+ var (x,,) = g();
+ var (,y,) = g();
+ var () = g();
+ var (,,) = g();
+ }
+ function g() returns (uint, uint, uint) {}
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(tuples)
+{
+ char const* text = R"(
+ contract C {
+ function f() {
+ uint a = (1);
+ var (b,) = (1,);
+ var (c,d) = (1, 2 + a);
+ var (e,) = (1, 2, b);
+ (a) = 3;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
+{
+ char const* text = R"(
+ contract C {
+ struct S { uint a; uint b; uint[][][] c; }
+ function f() {
+ C.S x;
+ C.S memory y;
+ C.S[10] memory z;
+ C.S[10](x);
+ x.a = 2;
+ x.c[1][2][3] = 9;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(using_for)
+{
+ char const* text = R"(
+ contract C {
+ struct s { uint a; }
+ using LibraryName for uint;
+ using Library2 for *;
+ using Lib for s;
+ function f() {
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(complex_import)
+{
+ char const* text = R"(
+ import "abc" as x;
+ import * as x from "abc";
+ import {a as b, c as d, f} from "def";
+ contract x {}
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(from_is_not_keyword)
+{
+ // "from" is not a keyword although it is used as a keyword in import directives.
+ char const* text = R"(
+ contract from {
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_declaration)
+{
+ char const* text = R"(
+ contract c {
+ uint[] a;
+ function f() returns (uint, uint) {
+ a = [1,2,3];
+ return (a[3], [2,3,4][0]);
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+
+BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_lvalue)
+{
+ char const* text = R"(
+ contract c {
+ uint[] a;
+ function f() returns (uint) {
+ a = [,2,3];
+ return (a[0]);
+ }
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_without_lvalue)
+{
+ char const* text = R"(
+ contract c {
+ uint[] a;
+ function f() returns (uint, uint) {
+ return ([3, ,4][0]);
+ }
+ }
+ )";
+ BOOST_CHECK(!successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_true_false_literal)
+{
+ char const* text = R"(
+ contract A {
+ function f() {
+ uint x = true ? 1 : 0;
+ uint y = false ? 0 : 1;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_with_constants)
+{
+ char const* text = R"(
+ contract A {
+ function f() {
+ uint x = 3 > 0 ? 3 : 0;
+ uint y = (3 > 0) ? 3 : 0;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_with_variables)
+{
+ char const* text = R"(
+ contract A {
+ function f() {
+ uint x = 3;
+ uint y = 1;
+ uint z = (x > y) ? x : y;
+ uint w = x > y ? x : y;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_multiple)
+{
+ char const* text = R"(
+ contract A {
+ function f() {
+ uint x = 3 < 0 ? 2 > 1 ? 2 : 1 : 7 > 2 ? 7 : 6;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_CASE(conditional_with_assignment)
+{
+ char const* text = R"(
+ contract A {
+ function f() {
+ uint y = 1;
+ uint x = 3 < 0 ? x = 3 : 6;
+ true ? x = 3 : 4;
+ }
+ }
+ )";
+ BOOST_CHECK(successParse(text));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp
new file mode 100644
index 00000000..10f30b8e
--- /dev/null
+++ b/test/libsolidity/SolidityScanner.cpp
@@ -0,0 +1,298 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Unit tests for the solidity scanner.
+ */
+
+#include <libsolidity/parsing/Scanner.h>
+#include <boost/test/unit_test.hpp>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(SolidityScanner)
+
+BOOST_AUTO_TEST_CASE(test_empty)
+{
+ Scanner scanner(CharStream(""));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Break);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "765");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "string1");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Comma);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "string2");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "identifier1");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(string_escapes)
+{
+ Scanner scanner(CharStream(" { \"a\\x61\""));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa");
+}
+
+BOOST_AUTO_TEST_CASE(string_escapes_with_zero)
+{
+ Scanner scanner(CharStream(" { \"a\\x61\\x00abc\""));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("aa\0abc", 6));
+}
+
+BOOST_AUTO_TEST_CASE(string_escape_illegal)
+{
+ Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "");
+ // TODO recovery from illegal tokens should be improved
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(hex_numbers)
+{
+ Scanner scanner(CharStream("var x = 0x765432536763762734623472346;"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x765432536763762734623472346");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(negative_numbers)
+{
+ Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9;"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Assign);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), ".2");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x78");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "7.3");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLiteral(), "8.9");
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(locations)
+{
+ Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().start, 0);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().end, 19);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().start, 20);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().end, 23);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().start, 24);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().end, 25);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Sub);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Number);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().start, 27);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().end, 32);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().start, 45);
+ BOOST_CHECK_EQUAL(scanner.currentLocation().end, 50);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+}
+
+BOOST_AUTO_TEST_CASE(ambiguities)
+{
+ // test scanning of some operators which need look-ahead
+ Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LessThanOrEqual);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Add);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::AssignAdd);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Inc);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SHL);
+}
+
+BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin)
+{
+ Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin)
+{
+ Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(documentation_comments_parsed)
+{
+ Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed)
+{
+ Scanner scanner(CharStream("some other tokens /**\n"
+ "* Send $(value / 1000) chocolates to the user\n"
+ "*/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars)
+{
+ Scanner scanner(CharStream("some other tokens /**\n"
+ " Send $(value / 1000) chocolates to the user\n"
+ "*/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell)
+{
+ Scanner scanner(CharStream("some other tokens /** \t \r \n"
+ "\t \r * Send $(value / 1000) chocolates to the user\n"
+ "*/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
+}
+
+BOOST_AUTO_TEST_CASE(comment_before_eos)
+{
+ Scanner scanner(CharStream("//"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "");
+}
+
+BOOST_AUTO_TEST_CASE(documentation_comment_before_eos)
+{
+ Scanner scanner(CharStream("///"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "");
+}
+
+BOOST_AUTO_TEST_CASE(empty_multiline_comment)
+{
+ Scanner scanner(CharStream("/**/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "");
+}
+
+BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos)
+{
+ Scanner scanner(CharStream("/***/"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::EOS);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "");
+}
+
+BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence)
+{
+ Scanner scanner(CharStream("hello_world ///documentation comment \n"
+ "//simple comment \n"
+ "<<"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SHL);
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "documentation comment ");
+}
+
+BOOST_AUTO_TEST_CASE(ether_subdenominations)
+{
+ Scanner scanner(CharStream("wei szabo finney ether"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther);
+}
+
+BOOST_AUTO_TEST_CASE(time_subdenominations)
+{
+ Scanner scanner(CharStream("seconds minutes hours days weeks years"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubSecond);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubMinute);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubHour);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubDay);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubWeek);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::SubYear);
+}
+
+BOOST_AUTO_TEST_CASE(time_after)
+{
+ Scanner scanner(CharStream("after 1"));
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::After);
+}
+
+BOOST_AUTO_TEST_CASE(empty_comment)
+{
+ Scanner scanner(CharStream("//\ncontract{}"));
+ BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "");
+ BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Contract);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::LBrace);
+ BOOST_CHECK_EQUAL(scanner.next(), Token::RBrace);
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp
new file mode 100644
index 00000000..87dda9c2
--- /dev/null
+++ b/test/libsolidity/SolidityTypes.cpp
@@ -0,0 +1,93 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Unit tests for the type system of Solidity.
+ */
+
+#include <libsolidity/ast/Types.h>
+#include <boost/test/unit_test.hpp>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(SolidityTypes)
+
+BOOST_AUTO_TEST_CASE(storage_layout_simple)
+{
+ MemberList members(MemberList::MemberMap({
+ {string("first"), Type::fromElementaryTypeName("uint128")},
+ {string("second"), Type::fromElementaryTypeName("uint120")},
+ {string("wraps"), Type::fromElementaryTypeName("uint16")}
+ }));
+ BOOST_REQUIRE_EQUAL(u256(2), members.storageSize());
+ BOOST_REQUIRE(members.memberStorageOffset("first") != nullptr);
+ BOOST_REQUIRE(members.memberStorageOffset("second") != nullptr);
+ BOOST_REQUIRE(members.memberStorageOffset("wraps") != nullptr);
+ BOOST_CHECK(*members.memberStorageOffset("first") == make_pair(u256(0), unsigned(0)));
+ BOOST_CHECK(*members.memberStorageOffset("second") == make_pair(u256(0), unsigned(16)));
+ BOOST_CHECK(*members.memberStorageOffset("wraps") == make_pair(u256(1), unsigned(0)));
+}
+
+BOOST_AUTO_TEST_CASE(storage_layout_mapping)
+{
+ MemberList members(MemberList::MemberMap({
+ {string("first"), Type::fromElementaryTypeName("uint128")},
+ {string("second"), make_shared<MappingType>(
+ Type::fromElementaryTypeName("uint8"),
+ Type::fromElementaryTypeName("uint8")
+ )},
+ {string("third"), Type::fromElementaryTypeName("uint16")},
+ {string("final"), make_shared<MappingType>(
+ Type::fromElementaryTypeName("uint8"),
+ Type::fromElementaryTypeName("uint8")
+ )},
+ }));
+ BOOST_REQUIRE_EQUAL(u256(4), members.storageSize());
+ BOOST_REQUIRE(members.memberStorageOffset("first") != nullptr);
+ BOOST_REQUIRE(members.memberStorageOffset("second") != nullptr);
+ BOOST_REQUIRE(members.memberStorageOffset("third") != nullptr);
+ BOOST_REQUIRE(members.memberStorageOffset("final") != nullptr);
+ BOOST_CHECK(*members.memberStorageOffset("first") == make_pair(u256(0), unsigned(0)));
+ BOOST_CHECK(*members.memberStorageOffset("second") == make_pair(u256(1), unsigned(0)));
+ BOOST_CHECK(*members.memberStorageOffset("third") == make_pair(u256(2), unsigned(0)));
+ BOOST_CHECK(*members.memberStorageOffset("final") == make_pair(u256(3), unsigned(0)));
+}
+
+BOOST_AUTO_TEST_CASE(storage_layout_arrays)
+{
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(1), 32).storageSize() == 1);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(1), 33).storageSize() == 2);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(2), 31).storageSize() == 2);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(7), 8).storageSize() == 2);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(7), 9).storageSize() == 3);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(31), 9).storageSize() == 9);
+ BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(32), 9).storageSize() == 9);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h
new file mode 100644
index 00000000..a5549d5b
--- /dev/null
+++ b/test/libsolidity/solidityExecutionFramework.h
@@ -0,0 +1,311 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Framework for executing Solidity contracts and testing them against C++ implementation.
+ */
+
+#pragma once
+
+#include <string>
+#include <tuple>
+#include "../TestHelper.h"
+#include <libethcore/ABI.h>
+#include <libethcore/SealEngine.h>
+#include <libethereum/State.h>
+#include <libethereum/Executive.h>
+#include <libethereum/ChainParams.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/interface/Exceptions.h>
+
+namespace dev
+{
+
+namespace solidity
+{
+namespace test
+{
+
+class ExecutionFramework
+{
+public:
+ ExecutionFramework():
+ m_sealEngine(eth::ChainParams().createSealEngine()),
+ m_state(0)
+ {
+ if (g_logVerbosity != -1)
+ g_logVerbosity = 0;
+ //m_state.resetCurrent();
+ }
+
+ bytes const& compileAndRunWithoutCheck(
+ std::string const& _sourceCode,
+ u256 const& _value = 0,
+ std::string const& _contractName = "",
+ bytes const& _arguments = bytes(),
+ std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>()
+ )
+ {
+ m_compiler.reset(false, m_addStandardSources);
+ m_compiler.addSource("", _sourceCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
+ eth::LinkerObject obj = m_compiler.object(_contractName);
+ obj.link(_libraryAddresses);
+ BOOST_REQUIRE(obj.linkReferences.empty());
+ sendMessage(obj.bytecode + _arguments, true, _value);
+ return m_output;
+ }
+
+ bytes const& compileAndRun(
+ std::string const& _sourceCode,
+ u256 const& _value = 0,
+ std::string const& _contractName = "",
+ bytes const& _arguments = bytes(),
+ std::map<std::string, Address> const& _libraryAddresses = std::map<std::string, Address>()
+ )
+ {
+ compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments, _libraryAddresses);
+ BOOST_REQUIRE(!m_output.empty());
+ return m_output;
+ }
+
+ template <class... Args>
+ bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments)
+ {
+ FixedHash<4> hash(dev::sha3(_sig));
+ sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value);
+ return m_output;
+ }
+
+ template <class... Args>
+ bytes const& callContractFunction(std::string _sig, Args const&... _arguments)
+ {
+ return callContractFunctionWithValue(_sig, 0, _arguments...);
+ }
+
+ template <class CppFunction, class... Args>
+ void testSolidityAgainstCpp(std::string _sig, CppFunction const& _cppFunction, Args const&... _arguments)
+ {
+ bytes solidityResult = callContractFunction(_sig, _arguments...);
+ bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
+ BOOST_CHECK_MESSAGE(
+ solidityResult == cppResult,
+ "Computed values do not match.\nSolidity: " +
+ toHex(solidityResult) +
+ "\nC++: " +
+ toHex(cppResult));
+ }
+
+ template <class CppFunction, class... Args>
+ void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction, u256 const& _rangeStart, u256 const& _rangeEnd)
+ {
+ for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
+ {
+ bytes solidityResult = callContractFunction(_sig, argument);
+ bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
+ BOOST_CHECK_MESSAGE(
+ solidityResult == cppResult,
+ "Computed values do not match.\nSolidity: " +
+ toHex(solidityResult) +
+ "\nC++: " +
+ toHex(cppResult) +
+ "\nArgument: " +
+ toHex(encode(argument))
+ );
+ }
+ }
+
+ static bytes encode(bool _value) { return encode(byte(_value)); }
+ static bytes encode(int _value) { return encode(u256(_value)); }
+ static bytes encode(size_t _value) { return encode(u256(_value)); }
+ static bytes encode(char const* _value) { return encode(std::string(_value)); }
+ static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; }
+ static bytes encode(u256 const& _value) { return toBigEndian(_value); }
+ static bytes encode(h256 const& _value) { return _value.asBytes(); }
+ static bytes encode(bytes const& _value, bool _padLeft = true)
+ {
+ bytes padding = bytes((32 - _value.size() % 32) % 32, 0);
+ return _padLeft ? padding + _value : _value + padding;
+ }
+ static bytes encode(std::string const& _value) { return encode(asBytes(_value), false); }
+ template <class _T>
+ static bytes encode(std::vector<_T> const& _value)
+ {
+ bytes ret;
+ for (auto const& v: _value)
+ ret += encode(v);
+ return ret;
+ }
+
+ template <class FirstArg, class... Args>
+ static bytes encodeArgs(FirstArg const& _firstArg, Args const&... _followingArgs)
+ {
+ return encode(_firstArg) + encodeArgs(_followingArgs...);
+ }
+ static bytes encodeArgs()
+ {
+ return bytes();
+ }
+ //@todo might be extended in the future
+ template <class Arg>
+ static bytes encodeDyn(Arg const& _arg)
+ {
+ return encodeArgs(u256(0x20), u256(_arg.size()), _arg);
+ }
+
+ class ContractInterface
+ {
+ public:
+ ContractInterface(ExecutionFramework& _framework): m_framework(_framework) {}
+
+ void setNextValue(u256 const& _value) { m_nextValue = _value; }
+
+ protected:
+ template <class... Args>
+ bytes const& call(std::string const& _sig, Args const&... _arguments)
+ {
+ auto const& ret = m_framework.callContractFunctionWithValue(_sig, m_nextValue, _arguments...);
+ m_nextValue = 0;
+ return ret;
+ }
+
+ void callString(std::string const& _name, std::string const& _arg)
+ {
+ BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty());
+ }
+
+ void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2)
+ {
+ BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
+ }
+
+ void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3)
+ {
+ BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty());
+ }
+
+ void callStringBytes32(std::string const& _name, std::string const& _arg1, h256 const& _arg2)
+ {
+ BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
+ }
+
+ u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg)
+ {
+ bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
+ BOOST_REQUIRE(ret.size() == 0x20);
+ BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12);
+ return eth::abiOut<u160>(ret);
+ }
+
+ std::string callAddressReturnsString(std::string const& _name, u160 const& _arg)
+ {
+ bytesConstRef ret = ref(call(_name + "(address)", _arg));
+ BOOST_REQUIRE(ret.size() >= 0x20);
+ u256 offset = eth::abiOut<u256>(ret);
+ BOOST_REQUIRE_EQUAL(offset, 0x20);
+ u256 len = eth::abiOut<u256>(ret);
+ BOOST_REQUIRE_EQUAL(ret.size(), ((len + 0x1f) / 0x20) * 0x20);
+ return ret.cropped(0, size_t(len)).toString();
+ }
+
+ h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg)
+ {
+ bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
+ BOOST_REQUIRE(ret.size() == 0x20);
+ return eth::abiOut<h256>(ret);
+ }
+
+ private:
+ u256 m_nextValue;
+ ExecutionFramework& m_framework;
+ };
+
+private:
+ template <class CppFunction, class... Args>
+ auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
+ -> typename std::enable_if<std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
+ {
+ _cppFunction(_arguments...);
+ return bytes();
+ }
+ template <class CppFunction, class... Args>
+ auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
+ -> typename std::enable_if<!std::is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
+ {
+ return encode(_cppFunction(_arguments...));
+ }
+
+protected:
+ void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
+ {
+ m_state.addBalance(m_sender, _value); // just in case
+ eth::Executive executive(m_state, m_envInfo, m_sealEngine.get());
+ eth::ExecutionResult res;
+ executive.setResultRecipient(res);
+ eth::Transaction t =
+ _isCreation ?
+ eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) :
+ eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
+ bytes transactionRLP = t.rlp();
+ try
+ {
+ // this will throw since the transaction is invalid, but it should nevertheless store the transaction
+ executive.initialize(&transactionRLP);
+ executive.execute();
+ }
+ catch (...) {}
+ if (_isCreation)
+ {
+ BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender));
+ m_contractAddress = executive.newAddress();
+ BOOST_REQUIRE(m_contractAddress);
+ BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
+ }
+ else
+ {
+ BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
+ BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas));
+ }
+ BOOST_REQUIRE(executive.go(/* DEBUG eth::Executive::simpleTrace() */));
+ m_state.noteSending(m_sender);
+ executive.finalize();
+ m_gasUsed = res.gasUsed;
+ m_output = std::move(res.output);
+ m_logs = executive.logs();
+ }
+
+ std::unique_ptr<eth::SealEngineFace> m_sealEngine;
+ size_t m_optimizeRuns = 200;
+ bool m_optimize = false;
+ bool m_addStandardSources = false;
+ dev::solidity::CompilerStack m_compiler;
+ Address m_sender;
+ Address m_contractAddress;
+ eth::EnvInfo m_envInfo;
+ eth::State m_state;
+ u256 const m_gasPrice = 100 * eth::szabo;
+ u256 const m_gas = 100000000;
+ bytes m_output;
+ eth::LogEntries m_logs;
+ u256 m_gasUsed;
+};
+
+}
+}
+} // end namespaces
+