Merge pull request #1782 from ethereum/develop

Solidity 0.4.10
This commit is contained in:
chriseth 2017-03-15 18:07:52 +01:00 committed by GitHub
commit f0d539ae05
122 changed files with 3609 additions and 1492 deletions

View File

@ -21,10 +21,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/> # along with solidity. If not, see <http://www.gnu.org/licenses/>
# #
# (c) 2016 solidity contributors. # (c) 2016-2017 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
language: cpp language: cpp
branches: branches:
# We need to whitelist the branches which we want to have "push" automation, # We need to whitelist the branches which we want to have "push" automation,
# this includes tags (which are treated as branches by travis). # this includes tags (which are treated as branches by travis).
@ -33,6 +34,18 @@ branches:
- develop - develop
- release - release
- /^v[0-9]/ - /^v[0-9]/
env:
global:
- ENCRYPTION_LABEL="6d4541b72666"
- SOLC_BUILD_TYPE=RelWithDebInfo
- SOLC_DOCS=Off
- SOLC_EMSCRIPTEN=Off
- SOLC_INSTALL_DEPS_TRAVIS=On
- SOLC_RELEASE=On
- SOLC_TESTS=On
- SOLC_DOCKER=Off
matrix: matrix:
include: include:
# Ubuntu 14.04 LTS "Trusty Tahr" # Ubuntu 14.04 LTS "Trusty Tahr"
@ -61,10 +74,24 @@ matrix:
dist: trusty dist: trusty
sudo: required sudo: required
compiler: gcc compiler: gcc
before_install:
- sudo apt-get -y install python-sphinx
env: env:
- TRAVIS_DOCS=On - SOLC_DOCS=On
- TRAVIS_RELEASE=Off - SOLC_RELEASE=Off
- TRAVIS_TESTS=Off - SOLC_TESTS=Off
# Docker target, which generates a statically linked alpine image
- os: linux
dist: trusty
sudo: required
services:
- docker
env:
- SOLC_DOCKER=On
- SOLC_INSTALL_DEPS_TRAVIS=Off
- SOLC_RELEASE=Off
- SOLC_TESTS=Off
# Emscripten target, which compiles 'solc' to javascript and uploads the resulting .js # Emscripten target, which compiles 'solc' to javascript and uploads the resulting .js
# files to https://github.com/ethereum/solc-bin. These binaries are used in Browser-Solidity # files to https://github.com/ethereum/solc-bin. These binaries are used in Browser-Solidity
@ -73,16 +100,19 @@ matrix:
dist: trusty dist: trusty
sudo: required sudo: required
compiler: gcc compiler: gcc
node_js: stable node_js:
- "6"
services: services:
- docker - docker
before_install: before_install:
- nvm install 6
- nvm use 6
- docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit
env: env:
- TRAVIS_EMSCRIPTEN=On - SOLC_EMSCRIPTEN=On
- TRAVIS_INSTALL_DEPS=Off - SOLC_INSTALL_DEPS_TRAVIS=Off
- TRAVIS_RELEASE=Off - SOLC_RELEASE=Off
- TRAVIS_TESTS=Off - SOLC_TESTS=Off
# OS X Mavericks (10.9) # OS X Mavericks (10.9)
# https://en.wikipedia.org/wiki/OS_X_Mavericks # https://en.wikipedia.org/wiki/OS_X_Mavericks
@ -101,7 +131,7 @@ matrix:
# env: # env:
# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" # # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?"
# # https://github.com/ethereum/solidity/issues/894 # # https://github.com/ethereum/solidity/issues/894
# - TRAVIS_TESTS=Off # - SOLC_TESTS=Off
# - ZIP_SUFFIX=osx-yosemite # - ZIP_SUFFIX=osx-yosemite
# OS X El Capitan (10.11) # OS X El Capitan (10.11)
@ -112,10 +142,10 @@ matrix:
# env: # env:
# # The use of Debug config here ONLY for El Capitan is a workaround for "The Heisenbug" # # The use of Debug config here ONLY for El Capitan is a workaround for "The Heisenbug"
# # See https://github.com/ethereum/webthree-umbrella/issues/565 # # See https://github.com/ethereum/webthree-umbrella/issues/565
# - TRAVIS_BUILD_TYPE=Debug # - SOLC_BUILD_TYPE=Debug
# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" # # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?"
# # https://github.com/ethereum/solidity/issues/894 # # https://github.com/ethereum/solidity/issues/894
# - TRAVIS_TESTS=Off # - SOLC_TESTS=Off
# - ZIP_SUFFIX=osx-elcapitan # - ZIP_SUFFIX=osx-elcapitan
# macOS Sierra (10.12) # macOS Sierra (10.12)
@ -126,10 +156,10 @@ matrix:
# env: # env:
# # Look like "The Heisenbug" is occurring here too, so we'll do the same workaround. # # Look like "The Heisenbug" is occurring here too, so we'll do the same workaround.
# # See https://travis-ci.org/ethereum/solidity/jobs/150240930 # # See https://travis-ci.org/ethereum/solidity/jobs/150240930
# - TRAVIS_BUILD_TYPE=Debug # - SOLC_BUILD_TYPE=Debug
# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" # # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?"
# # https://github.com/ethereum/solidity/issues/894 # # https://github.com/ethereum/solidity/issues/894
# - TRAVIS_TESTS=Off # - SOLC_TESTS=Off
# - ZIP_SUFFIX=macos-sierra # - ZIP_SUFFIX=macos-sierra
git: git:
@ -143,41 +173,20 @@ cache:
- $HOME/.local - $HOME/.local
install: install:
- test $TRAVIS_INSTALL_DEPS != On || ./scripts/install_deps.sh - test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh)
- test "$TRAVIS_OS_NAME" != "linux" || ./scripts/install_cmake.sh - test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh)
- echo -n "$TRAVIS_COMMIT" > commit_hash.txt - echo -n "$TRAVIS_COMMIT" > commit_hash.txt
before_script: - test $SOLC_DOCKER != On || (docker build -t ethereum/solc:build -f scripts/Dockerfile .)
- test $TRAVIS_EMSCRIPTEN != On || ./scripts/build_emscripten.sh
- test $TRAVIS_RELEASE != On || (mkdir -p build
&& cd build
&& cmake .. -DCMAKE_BUILD_TYPE=$TRAVIS_BUILD_TYPE
&& make -j2
&& cd ..
&& ./scripts/release.sh $ZIP_SUFFIX
&& ./scripts/create_source_tarball.sh )
script:
- test $TRAVIS_DOCS != On || ./scripts/docs.sh
# There are a variety of reliability issues with the Solidity unit-tests at the time of before_script:
# writing (especially on macOS), so within TravisCI we will try to run the unit-tests - test $SOLC_EMSCRIPTEN != On || (scripts/build_emscripten.sh)
# up to 3 times before giving up and declaring the tests as broken. - test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE
# && scripts/release.sh $ZIP_SUFFIX
# We should aim to remove this "retry logic" as soon as we can, because it is a && scripts/create_source_tarball.sh)
# band-aid for issues which need solving at their root. Some of those issues will be
# in Solidity's RPC setup and some will be in 'eth'. It seems unlikely that Solidity script:
# itself is broken from the failure messages which we are seeing. - test $SOLC_DOCS != On || (scripts/docs.sh)
# - test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh)
# More details on known issues at https://github.com/ethereum/solidity/issues/769
- test $TRAVIS_TESTS != On || (cd $TRAVIS_BUILD_DIR && (./scripts/tests.sh || ./scripts/tests.sh || ./scripts/tests.sh) )
env:
global:
- ENCRYPTION_LABEL="6d4541b72666"
- TRAVIS_BUILD_TYPE=RelWithDebInfo
- TRAVIS_DOCS=Off
- TRAVIS_EMSCRIPTEN=Off
- TRAVIS_INSTALL_DEPS=On
- TRAVIS_RELEASE=On
- TRAVIS_TESTS=On
deploy: deploy:
# This is the deploy target for the Emscripten build. # This is the deploy target for the Emscripten build.
@ -186,14 +195,24 @@ deploy:
# Both the build and deploy steps for Emscripten are only run within the Ubuntu # Both the build and deploy steps for Emscripten are only run within the Ubuntu
# configurations (not for macOS). That is controlled by conditionals within the bash # configurations (not for macOS). That is controlled by conditionals within the bash
# scripts because TravisCI doesn't provide much in the way of conditional logic. # scripts because TravisCI doesn't provide much in the way of conditional logic.
- provider: script - provider: script
script: test $TRAVIS_EMSCRIPTEN != On || scripts/release_emscripten.sh script: test $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh)
skip_cleanup: true
on:
branch:
- develop
- release
# This is the deploy target for the dockerfile. If we are pushing into a develop branch, it will be tagged
# as a nightly and appended the commit of the branch it was pushed in. If we are pushing to master it will
# be tagged as "stable" and given the version tag as well.
- provider: script
script: test $SOLC_DOCKER != On || (scripts/docker_deploy.sh)
skip_cleanup: true skip_cleanup: true
on: on:
branch: branch:
- develop - develop
- release - release
# This is the deploy target for the native build (Linux and macOS) # This is the deploy target for the native build (Linux and macOS)
# which generates ZIPs per commit and the source tarball. # which generates ZIPs per commit and the source tarball.
# #
@ -211,4 +230,4 @@ deploy:
on: on:
all_branches: true all_branches: true
tags: true tags: true
condition: $TRAVIS_RELEASE == On condition: $SOLC_RELEASE == On

View File

@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.4.9") set(PROJECT_VERSION "0.4.10")
project(solidity VERSION ${PROJECT_VERSION}) project(solidity VERSION ${PROJECT_VERSION})
# Let's find our dependencies # Let's find our dependencies

View File

@ -1,3 +1,34 @@
### 0.4.10 (2017-03-15)
Features:
* Add ``assert(condition)``, which throws if condition is false (meant for internal errors).
* Add ``require(condition)``, which throws if condition is false (meant for invalid input).
* Commandline interface: Do not overwrite files unless forced.
* Introduce ``.transfer(value)`` for sending Ether.
* Code generator: Support ``revert()`` to abort with rolling back, but not consuming all gas.
* Inline assembly: Support ``revert`` (EIP140) as an opcode.
* Parser: Support scientific notation in numbers (e.g. ``2e8`` and ``200e-2``).
* Type system: Support explicit conversion of external function to address.
* Type system: Warn if base of exponentiation is literal (result type might be unexpected).
* Type system: Warn if constant state variables are not compile-time constants.
Bugfixes:
* Commandline interface: Always escape filenames (replace ``/``, ``:`` and ``.`` with ``_``).
* Commandline interface: Do not try creating paths ``.`` and ``..``.
* Commandline interface: Allow long library names.
* Parser: Disallow octal literals.
* Type system: Fix a crash caused by continuing on fatal errors in the code.
* Type system: Disallow compound assignment for tuples.
* Type system: Detect cyclic dependencies between constants.
* Type system: Disallow arrays with negative length.
* Type system: Fix a crash related to invalid binary operators.
* Type system: Disallow ``var`` declaration with empty tuple type.
* Type system: Correctly convert function argument types to pointers for member functions.
* Type system: Move privateness of constructor into AST itself.
* Inline assembly: Charge one stack slot for non-value types during analysis.
* Assembly output: Print source location before the operation it refers to instead of after.
* Optimizer: Stop trying to optimize tricky constants after a while.
### 0.4.9 (2017-01-31) ### 0.4.9 (2017-01-31)
Features: Features:

View File

@ -62,7 +62,7 @@ test_script:
- ps: Start-Sleep -s 100 - ps: Start-Sleep -s 100
- cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION%
- copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" . - copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" .
- soltest.exe -- --ipcpath \\.\pipe\geth.ipc - soltest.exe --show-progress -- --ipcpath \\.\pipe\geth.ipc
artifacts: artifacts:
- path: solidity-windows.zip - path: solidity-windows.zip

View File

@ -71,7 +71,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
add_compile_options(-fPIC) add_compile_options(-fPIC)
# Configuration-specific compiler settings. # Configuration-specific compiler settings.
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

View File

@ -37,8 +37,9 @@ arising when writing manual assembly by the following features:
We now want to describe the inline assembly language in detail. We now want to describe the inline assembly language in detail.
.. warning:: .. warning::
Inline assembly is still a relatively new feature and might change if it does not prove useful, Inline assembly is a way to access the Ethereum Virtual Machine
so please try to keep up to date. at a low level. This discards several important safety
features of Solidity.
Example Example
------- -------
@ -49,6 +50,8 @@ idea is that assembly libraries will be used to enhance the language in such way
.. code:: .. code::
pragma solidity ^0.4.0;
library GetCode { library GetCode {
function at(address _addr) returns (bytes o_code) { function at(address _addr) returns (bytes o_code) {
assembly { assembly {
@ -69,11 +72,13 @@ idea is that assembly libraries will be used to enhance the language in such way
Inline assembly could also be beneficial in cases where the optimizer fails to produce Inline assembly could also be beneficial in cases where the optimizer fails to produce
efficient code. Please be aware that assembly is much more difficult to write because efficient code. Please be aware that assembly is much more difficult to write because
the compiler does not perform checks, so you should use it only if the compiler does not perform checks, so you should use it for complex things only if
you really know what you are doing. you really know what you are doing.
.. code:: .. code::
pragma solidity ^0.4.0;
library VectorSum { library VectorSum {
// This function is less efficient because the optimizer currently fails to // This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access. // remove the bounds checks in array access.
@ -104,7 +109,7 @@ these curly braces, the following can be used (see the later sections for more d
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below - opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
- opcode in functional style, e.g. ``add(1, mlod(0))`` - opcodes in functional style, e.g. ``add(1, mlod(0))``
- labels, e.g. ``name:`` - labels, e.g. ``name:``
- variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)`` - variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)``
- identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add`` - identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add``
@ -119,7 +124,7 @@ This document does not want to be a full description of the Ethereum virtual mac
following list can be used as a reference of its opcodes. following list can be used as a reference of its opcodes.
If an opcode takes arguments (always from the top of the stack), they are given in parentheses. If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
Note that the order of arguments can be seed to be reversed in non-functional style (explained below). Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
special and all others push exactly one item onte the stack. special and all others push exactly one item onte the stack.
@ -185,7 +190,7 @@ In the grammar, opcodes are represented as pre-defined identifiers.
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| pc | | current position in code | | pc | | current position in code |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| pop | `*` | remove topmost stack slot | | pop(x) | `-` | remove the element pushed by x |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | | dup1 ... dup16 | | copy ith stack slot to the top (counting from top) |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
@ -230,19 +235,24 @@ In the grammar, opcodes are represented as pre-defined identifiers.
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei | | create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
| | | and return the new address | | | | and return the new address |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)] | | call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
| insize, out, outsize) | | providing g gas and v wei and output area | | insize, out, outsize) | | providing g gas and v wei and output area |
| | | mem[out..(out+outsize)] returting 1 on error (out of gas) | | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
| | | and 1 on success |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| callcode(g, a, v, in, | | identical to call but only use the code from a and stay | | callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay |
| insize, out, outsize) | | in the context of the current contract otherwise | | insize, out, outsize) | | in the context of the current contract otherwise |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| delegatecall(g, a, in, | | identical to callcode but also keep ``caller`` | | delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` |
| insize, out, outsize) | | and ``callvalue`` | | insize, out, outsize) | | and ``callvalue`` |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| return(p, s) | `*` | end execution, return data mem[p..(p+s)) | | return(p, s) | `-` | end execution, return data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| selfdestruct(a) | `*` | end execution, destroy current contract and send funds to a | | revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a |
+-------------------------+------+-----------------------------------------------------------------+
| invalid | `-` | end execution with invalid instruction |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | | log0(p, s) | `-` | log without topics and data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+ +-------------------------+------+-----------------------------------------------------------------+
@ -323,14 +333,15 @@ push their entry label (with virtual function resolution applied). The calling s
in solidity are: in solidity are:
- the caller pushes return label, arg1, arg2, ..., argn - the caller pushes return label, arg1, arg2, ..., argn
- the call returns with ret1, ret2, ..., retn - the call returns with ret1, ret2, ..., retm
This feature is still a bit cumbersome to use, because the stack offset essentially This feature is still a bit cumbersome to use, because the stack offset essentially
changes during the call, and thus references to local variables will be wrong. changes during the call, and thus references to local variables will be wrong.
It is planned that the stack height changes can be specified in inline assembly.
.. code:: .. code::
pragma solidity ^0.4.0;
contract C { contract C {
uint b; uint b;
function f(uint x) returns (uint r) { function f(uint x) returns (uint r) {
@ -349,7 +360,9 @@ Labels
Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses
which can change easily. Solidity inline assembly provides labels to make the use of which can change easily. Solidity inline assembly provides labels to make the use of
jumps easier. The following code computes an element in the Fibonacci series. jumps easier. Note that labels are a low-level feature and it is possible to write
efficient assembly without labels, just using assembly functions, loops and switch instructions
(see below). The following code computes an element in the Fibonacci series.
.. code:: .. code::
@ -379,12 +392,14 @@ will have a wrong impression about the stack height at label ``two``:
.. code:: .. code::
{ {
let x := 8
jump(two) jump(two)
one: one:
// Here the stack height is 1 (because we pushed 7), // Here the stack height is 2 (because we pushed x and 7),
// but the assembler thinks it is 0 because it reads // but the assembler thinks it is 1 because it reads
// from top to bottom. // from top to bottom.
// Accessing stack variables here will lead to errors. // Accessing the stack variable x here will lead to errors.
x := 9
jump(three) jump(three)
two: two:
7 // push something onto the stack 7 // push something onto the stack
@ -392,6 +407,35 @@ will have a wrong impression about the stack height at label ``two``:
three: three:
} }
This problem can be fixed by manually adjusting the stack height for the
assembler - you can provide a stack height delta that is added
to the stack height just prior to the label.
Note that you will not have to care about these things if you just use
loops and assembly-level functions.
As an example how this can be done in extreme cases, please see the following.
.. code::
{
let x := 8
jump(two)
0 // This code is unreachable but will adjust the stack height correctly
one:
x := 9 // Now x can be accessed properly.
jump(three)
pop // Similar negative correction.
two:
7 // push something onto the stack
jump(one)
three:
pop // We have to pop the manually pushed value here again.
}
.. note::
``invalidJumpLabel`` is a pre-defined label. Jumping to this location will always
result in an invalid jump, effectively aborting execution of the code.
Declaring Assembly-Local Variables Declaring Assembly-Local Variables
---------------------------------- ----------------------------------
@ -405,6 +449,8 @@ be just ``0``, but it can also be a complex functional-style expression.
.. code:: .. code::
pragma solidity ^0.4.0;
contract C { contract C {
function f(uint x) returns (uint b) { function f(uint x) returns (uint b) {
assembly { assembly {
@ -446,6 +492,9 @@ is performed by replacing the variable's value on the stack by the new value.
Switch Switch
------ ------
.. note::
Switch is not yet implemented.
You can use a switch statement as a very basic version of "if/else". You can use a switch statement as a very basic version of "if/else".
It takes the value of an expression and compares it to several constants. It takes the value of an expression and compares it to several constants.
The branch corresponding to the matching constant is taken. Contrary to the The branch corresponding to the matching constant is taken. Contrary to the
@ -473,6 +522,9 @@ case does require them.
Loops Loops
----- -----
.. note::
Loops are not yet implemented.
Assembly supports a simple for-style loop. For-style loops have Assembly supports a simple for-style loop. For-style loops have
a header containing an initializing part, a condition and a post-iteration a header containing an initializing part, a condition and a post-iteration
part. The condition has to be a functional-style expression, while part. The condition has to be a functional-style expression, while
@ -494,6 +546,9 @@ The following example computes the sum of an area in memory.
Functions Functions
--------- ---------
.. note::
Functions are not yet implemented.
Assembly allows the definition of low-level functions. These take their Assembly allows the definition of low-level functions. These take their
arguments (and a return PC) from the stack and also put the results onto the arguments (and a return PC) from the stack and also put the results onto the
stack. Calling a function looks the same way as executing a functional-style stack. Calling a function looks the same way as executing a functional-style
@ -542,7 +597,7 @@ Conventions in Solidity
In contrast to EVM assembly, Solidity knows types which are narrower than 256 bits, In contrast to EVM assembly, Solidity knows types which are narrower than 256 bits,
e.g. ``uint24``. In order to make them more efficient, most arithmetic operations just e.g. ``uint24``. In order to make them more efficient, most arithmetic operations just
treat them as 256 bit numbers and the higher-order bits are only cleaned at the treat them as 256-bit numbers and the higher-order bits are only cleaned at the
point where it is necessary, i.e. just shortly before they are written to memory point where it is necessary, i.e. just shortly before they are written to memory
or before comparisons are performed. This means that if you access such a variable or before comparisons are performed. This means that if you access such a variable
from within inline assembly, you might have to manually clean the higher order bits from within inline assembly, you might have to manually clean the higher order bits
@ -592,25 +647,19 @@ which follow very simple and regular scoping rules and cleanup of local variable
Scoping: An identifier that is declared (label, variable, function, assembly) Scoping: An identifier that is declared (label, variable, function, assembly)
is only visible in the block where it was declared (including nested blocks is only visible in the block where it was declared (including nested blocks
inside the current block). It is not legal to access local variables across inside the current block). It is not legal to access local variables across
function borders, even if they would be in scope. Shadowing is allowed, but function borders, even if they would be in scope. Shadowing is not allowed.
two identifiers with the same name cannot be declared in the same block.
Local variables cannot be accessed before they were declared, but labels, Local variables cannot be accessed before they were declared, but labels,
functions and assemblies can. Assemblies are special blocks that are used functions and assemblies can. Assemblies are special blocks that are used
for e.g. returning runtime code or creating contracts. No identifier from an for e.g. returning runtime code or creating contracts. No identifier from an
outer assembly is visible in a sub-assembly. outer assembly is visible in a sub-assembly.
If control flow passes over the end of a block, pop instructions are inserted If control flow passes over the end of a block, pop instructions are inserted
that match the number of local variables declared in that block, unless the that match the number of local variables declared in that block.
``}`` is directly preceded by an opcode that does not have a continuing control Whenever a local variable is referenced, the code generator needs
flow path. Whenever a local variable is referenced, the code generator needs
to know its current relative position in the stack and thus it needs to to know its current relative position in the stack and thus it needs to
keep track of the current so-called stack height. keep track of the current so-called stack height. Since all local variables
At the end of a block, this implicit stack height is always reduced by the number are removed at the end of a block, the stack height before and after the block
of local variables whether ther is a continuing control flow or not. should be the same. If this is not the case, a warning is issued.
This means that the stack height before and after the block should be the same.
If this is not the case, a warning is issued,
unless the last instruction in the block did not have a continuing control flow path.
Why do we use higher-level constructs like ``switch``, ``for`` and functions: Why do we use higher-level constructs like ``switch``, ``for`` and functions:
@ -622,10 +671,9 @@ verification and optimization.
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated. Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
The position of all local variables on the stack needs to be known, otherwise The position of all local variables on the stack needs to be known, otherwise
neither references to local variables nor removing local variables automatically neither references to local variables nor removing local variables automatically
from the stack at the end of a block will work properly. Because of that, from the stack at the end of a block will work properly. The desugaring
every label that is preceded by an instruction that ends or diverts control flow mechanism correctly inserts operations at unreachable blocks that adjust the
should be annotated with the current stack layout. This annotation is performed stack height properly in case of jumps that do not have a continuing control flow.
automatically during the desugaring phase.
Example: Example:
@ -678,17 +726,21 @@ After the desugaring phase it looks as follows::
$case1: $case1:
{ {
// the function call - we put return label and arguments on the stack // the function call - we put return label and arguments on the stack
$ret1 calldataload(4) jump($fun_f) $ret1 calldataload(4) jump(f)
$ret1 [r]: // a label with a [...]-annotation resets the stack height // This is unreachable code. Opcodes are added that mirror the
// to "current block + number of local variables". It also // effect of the function on the stack height: Arguments are
// introduces a variable, r: // removed and return values are introduced.
// r is at top of stack, $0 is below (from enclosing block) pop pop
$ret2 0x20 jump($fun_allocate) let r := 0
$ret2 [ret]: // stack here: $0, r, ret (top) $ret1: // the actual return point
$ret2 0x20 jump($allocate)
pop pop let ret := 0
$ret2:
mstore(ret, r) mstore(ret, r)
return(ret, 0x20) return(ret, 0x20)
// although it is useless, the jump is automatically inserted, // although it is useless, the jump is automatically inserted,
// since the desugaring process does not analyze control-flow // since the desugaring process is a purely syntactic operation that
// does not analyze control-flow
jump($endswitch) jump($endswitch)
} }
$caseDefault: $caseDefault:
@ -699,20 +751,29 @@ After the desugaring phase it looks as follows::
$endswitch: $endswitch:
} }
jump($afterFunction) jump($afterFunction)
$fun_allocate: allocate:
{ {
$start[$retpos, size]: // we jump over the unreachable code that introduces the function arguments
// output variables live in the same scope as the arguments. jump($start)
let $retpos := 0 let size := 0
$start:
// output variables live in the same scope as the arguments and is
// actually allocated.
let pos := 0 let pos := 0
{ {
pos := mload(0x40) pos := mload(0x40)
mstore(0x40, add(pos, size)) mstore(0x40, add(pos, size))
} }
// This code replaces the arguments by the return values and jumps back.
swap1 pop swap1 jump swap1 pop swap1 jump
// Again unreachable code that corrects stack height.
0 0
} }
$fun_f: f:
{ {
start [$retpos, x]: jump($start)
let $retpos := 0 let x := 0
$start:
let y := 0 let y := 0
{ {
let i := 0 let i := 0
@ -725,8 +786,9 @@ After the desugaring phase it looks as follows::
{ i := add(i, 1) } { i := add(i, 1) }
jump($for_begin) jump($for_begin)
$for_end: $for_end:
} // Here, a pop instruction is inserted for i } // Here, a pop instruction will be inserted for i
swap1 pop swap1 jump swap1 pop swap1 jump
0 0
} }
$afterFunction: $afterFunction:
stop stop
@ -787,7 +849,7 @@ Grammar::
IdentifierOrList = Identifier | '(' IdentifierList ')' IdentifierOrList = Identifier | '(' IdentifierList ')'
IdentifierList = Identifier ( ',' Identifier)* IdentifierList = Identifier ( ',' Identifier)*
AssemblyAssignment = '=:' Identifier AssemblyAssignment = '=:' Identifier
LabelDefinition = Identifier ( '[' ( IdentifierList | NumberLiteral ) ']' )? ':' LabelDefinition = Identifier ':'
AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase*
( 'default' ':' AssemblyBlock )? ( 'default' ':' AssemblyBlock )?
AssemblyCase = 'case' FunctionalAssemblyExpression ':' AssemblyBlock AssemblyCase = 'case' FunctionalAssemblyExpression ':' AssemblyBlock
@ -820,11 +882,14 @@ Pseudocode::
AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) -> AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) ->
<name>: <name>:
{ {
$<name>_start [$retPC, $argn, ..., arg1]: jump($<name>_start)
let $retPC := 0 let argn := 0 ... let arg1 := 0
$<name>_start:
let ret1 := 0 ... let retm := 0 let ret1 := 0 ... let retm := 0
{ desugar(body) } { desugar(body) }
swap and pop items so that only ret1, ... retn, $retPC are left on the stack swap and pop items so that only ret1, ... retm, $retPC are left on the stack
jump jump
0 (1 + n times) to compensate removal of arg1, ..., argn and $retPC
} }
AssemblyFor('for' { init } condition post body) -> AssemblyFor('for' { init } condition post body) ->
{ {
@ -844,6 +909,7 @@ Pseudocode::
pop all local variables that are defined at the current point pop all local variables that are defined at the current point
but not at $forI_end but not at $forI_end
jump($forI_end) jump($forI_end)
0 (as many as variables were removed above)
} }
'continue' -> 'continue' ->
{ {
@ -851,6 +917,7 @@ Pseudocode::
pop all local variables that are defined at the current point pop all local variables that are defined at the current point
but not at $forI_continue but not at $forI_continue
jump($forI_continue) jump($forI_continue)
0 (as many as variables were removed above)
} }
AssemblySwitch(switch condition cases ( default: defaultBlock )? ) -> AssemblySwitch(switch condition cases ( default: defaultBlock )? ) ->
{ {
@ -872,10 +939,13 @@ Pseudocode::
{ {
// find I such that $funcallI_* does not exist // find I such that $funcallI_* does not exist
$funcallI_return argn ... arg2 arg1 jump(<name>) $funcallI_return argn ... arg2 arg1 jump(<name>)
pop (n + 1 times)
if the current context is `let (id1, ..., idm) := f(...)` -> if the current context is `let (id1, ..., idm) := f(...)` ->
$funcallI_return [id1, ..., idm]: let id1 := 0 ... let idm := 0
$funcallI_return:
else -> else ->
$funcallI_return[m - n - 1]: 0 (m times)
$funcallI_return:
turn the functional expression that leads to the function call turn the functional expression that leads to the function call
into a statement stream into a statement stream
} }
@ -888,8 +958,16 @@ Pseudocode::
Opcode Stream Generation Opcode Stream Generation
------------------------ ------------------------
During opcode stream generation, we keep track of the current stack height, During opcode stream generation, we keep track of the current stack height
so that accessing stack variables by name is possible. in a counter,
so that accessing stack variables by name is possible. The stack height is modified with every opcode
that modifies the stack and with every label that is annotated with a stack
adjustment. Every time a new
local variable is introduced, it is registered together with the current
stack height. If a variable is accessed (either for copying its value or for
assignment), the appropriate DUP or SWAP instruction is selected depending
on the difference bitween the current stack height and the
stack height at the point the variable was introduced.
Pseudocode:: Pseudocode::
@ -927,13 +1005,8 @@ Pseudocode::
look up id in the syntactic stack of blocks, assert that it is a variable look up id in the syntactic stack of blocks, assert that it is a variable
SWAPi where i = 1 + stack_height - stack_height_of_identifier(id) SWAPi where i = 1 + stack_height - stack_height_of_identifier(id)
POP POP
LabelDefinition(name [id1, ..., idn] :) -> LabelDefinition(name:) ->
JUMPDEST JUMPDEST
// register new variables id1, ..., idn and set the stack height to
// stack_height_at_block_start + number_of_local_variables
LabelDefinition(name [number] :) ->
JUMPDEST
// adjust stack height by +number (can be negative)
NumberLiteral(num) -> NumberLiteral(num) ->
PUSH<num interpreted as decimal and right-aligned> PUSH<num interpreted as decimal and right-aligned>
HexLiteral(lit) -> HexLiteral(lit) ->

View File

@ -81,7 +81,7 @@ This is as opposed to the more intuitive sending pattern:
mostSent = msg.value; mostSent = msg.value;
} }
function becomeRichest() returns (bool) { function becomeRichest() payable returns (bool) {
if (msg.value > mostSent) { if (msg.value > mostSent) {
// Check if call succeeds to prevent an attacker // Check if call succeeds to prevent an attacker
// from trapping the previous person's funds in // from trapping the previous person's funds in

View File

@ -15,6 +15,7 @@
import sys import sys
import os import os
import re
# If extensions (or modules to document with autodoc) are in another directory, # 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 # add these directories to sys.path here. If the directory is relative to the
@ -56,9 +57,14 @@ copyright = '2016-2017, Ethereum'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.4.9' with open('../CMakeLists.txt', 'r') as f:
version = re.search('PROJECT_VERSION "([^"]+)"', f.read()).group(1)
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.4.9-develop' if os.path.isfile('../prerelease.txt') != True or os.path.getsize('../prerelease.txt') == 0:
release = version
else:
# This is a prerelease version
release = version + '-develop'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -145,11 +145,11 @@ This means that cyclic creation dependencies are impossible.
.. index:: ! visibility, external, public, private, internal .. index:: ! visibility, external, public, private, internal
.. _visibility-and-accessors: .. _visibility-and-getters:
************************ **********************
Visibility and Accessors Visibility and Getters
************************ **********************
Since Solidity knows two kinds of function calls (internal Since Solidity knows two kinds of function calls (internal
ones that do not create an actual EVM call (also called ones that do not create an actual EVM call (also called
@ -173,7 +173,7 @@ and the default is ``internal``.
``public``: ``public``:
Public functions are part of the contract Public functions are part of the contract
interface and can be either called internally or via interface and can be either called internally or via
messages. For public state variables, an automatic accessor messages. For public state variables, an automatic getter
function (see below) is generated. function (see below) is generated.
``internal``: ``internal``:
@ -243,12 +243,12 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
} }
} }
.. index:: ! accessor;function, ! function;accessor .. index:: ! getter;function, ! function;getter
Accessor Functions Getter Functions
================== ================
The compiler automatically creates accessor functions for The compiler automatically creates getter functions for
all **public** state variables. For the contract given below, the compiler will all **public** state variables. For the contract given below, the compiler will
generate a function called ``data`` that does not take any generate a function called ``data`` that does not take any
arguments and returns a ``uint``, the value of the state arguments and returns a ``uint``, the value of the state
@ -271,7 +271,7 @@ be done at declaration.
} }
} }
The accessor functions have external visibility. If the The getter functions have external visibility. If the
symbol is accessed internally (i.e. without ``this.``), symbol is accessed internally (i.e. without ``this.``),
it is evaluated as a state variable and if it is accessed externally it is evaluated as a state variable and if it is accessed externally
(i.e. with ``this.``), it is evaluated as a function. (i.e. with ``this.``), it is evaluated as a function.
@ -428,8 +428,25 @@ change by overriding).
Constant State Variables Constant State Variables
************************ ************************
State variables can be declared as constant (this is not yet implemented State variables can be declared as ``constant``. In this case, they have to be
for array and struct types and not possible for mapping types). assigned from an expression which is a constant at compile time. Any expression
that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or
``block.number``) or
execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions
that might have a side-effect on memory allocation are allowed, but those that
might have a side-effect on other memory objects are not. The built-in functions
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
are allowed (ever though they do call external contracts).
The reason behind allowing side-effects on the memory allocator is that it
should be possible to construct complex objects like e.g. lookup-tables.
This feature is not yet fully usable.
The compiler does not reserve a storage slot for these variables and every occurrence is
replaced by the respective constant expression (which might be computed to a single value by the optimizer).
Not all types for constants are implemented at this time. The only supported types are
value types and strings.
:: ::
@ -438,12 +455,9 @@ for array and struct types and not possible for mapping types).
contract C { contract C {
uint constant x = 32**22 + 8; uint constant x = 32**22 + 8;
string constant text = "abc"; string constant text = "abc";
bytes32 constant myHash = keccak256("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.
****************** ******************
Constant Functions Constant Functions
@ -462,7 +476,7 @@ Functions can be declared constant. These functions promise not to modify the st
} }
.. note:: .. note::
Accessor methods are marked constant. Getter methods are marked constant.
.. warning:: .. warning::
The compiler does not enforce yet that a constant method is not modifying state. The compiler does not enforce yet that a constant method is not modifying state.
@ -882,7 +896,7 @@ Inheriting Different Kinds of Members of the Same Name
When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error. When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error.
This error is produced also by an event and a modifier of the same name, and a function and an event of the same name. This error is produced also by an event and a modifier of the same name, and a function and an event of the same name.
As an exception, a state variable accessor can override a public function. As an exception, a state variable getter can override a public function.
.. index:: ! contract;abstract, ! abstract contract .. index:: ! contract;abstract, ! abstract contract

View File

@ -393,487 +393,22 @@ Currently, Solidity automatically generates a runtime exception in the following
#. If you convert a value too big or negative into an enum type. #. If you convert a value too big or negative into an enum type.
#. If you perform an external function call targeting a contract that contains no code. #. If you perform an external function call targeting a contract that contains no code.
#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). #. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
#. If your contract receives Ether via a public accessor function. #. If your contract receives Ether via a public getter function.
#. If you call a zero-initialized variable of internal function type. #. If you call a zero-initialized variable of internal function type.
#. If a ``.transfer()`` fails.
#. If you call ``assert`` with an argument that evaluates to false.
Internally, Solidity performs an "invalid jump" when a user-provided exception is thrown. In contrast, it performs an invalid operation While a user-provided exception is generated in the following situations:
(instruction ``0xfe``) if a runtime exception is encountered. In both cases, this causes #. Calling ``throw``.
#. Calling ``require`` with an argument that evaluates to ``false``.
Internally, Solidity performs a revert operation (instruction ``0xfd``) when a user-provided exception is thrown or the condition of
a ``require`` call is not met. In contrast, it performs an invalid operation
(instruction ``0xfe``) if a runtime exception is encountered or the condition of an ``assert`` call is not met. In both cases, this 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 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 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. (or at least call) without effect.
.. index:: ! assembly, ! asm, ! evmasm If contracts are written so that ``assert`` is only used to test internal conditions and ``require``
is used in case of malformed input, a formal analysis tool that verifies that the invalid
Inline Assembly opcode can never be reached can be used to check for the absence of errors assuming valid inputs.
===============
For more fine-grained control especially in order to enhance the language by writing libraries,
it is possible to interleave Solidity statements with inline assembly in a language close
to the one of the virtual machine. Due to the fact that the EVM is a stack machine, it is
often hard to address the correct stack slot and provide arguments to opcodes at the correct
point on the stack. Solidity's inline assembly tries to facilitate that and other issues
arising when writing manual assembly by the following features:
* functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul``
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
* access to external variables: ``function f(uint x) { assembly { x := sub(x, 1) } }``
* labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))``
We now want to describe the inline assembly language in detail.
.. warning::
Inline assembly is a way to access the Ethereum Virtual Machine
at a low level. This discards several important safety
features of Solidity.
Example
-------
The following example provides library code to access the code of another contract and
load it into a ``bytes`` variable. This is not possible at all with "plain Solidity" and the
idea is that assembly libraries will be used to enhance the language in such ways.
.. code::
pragma solidity ^0.4.0;
library GetCode {
function at(address _addr) returns (bytes o_code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(o_code, 0x20), 0, size)
}
}
}
Inline assembly could also be beneficial in cases where the optimizer fails to produce
efficient code. Please be aware that assembly is much more difficult to write because
the compiler does not perform checks, so you should use it for complex things only if
you really know what you are doing.
.. code::
pragma solidity ^0.4.0;
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] _data) returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i)
o_sum += _data[i];
}
// We know that we only access the array in bounds, so we can avoid the check.
// 0x20 needs to be added to an array because the first slot contains the
// array length.
function sumAsm(uint[] _data) returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i) {
assembly {
o_sum := mload(add(add(_data, 0x20), i))
}
}
}
}
Syntax
------
Inline assembly parses comments, literals and identifiers exactly as Solidity, so you can use the
usual ``//`` and ``/* */`` comments. Inline assembly is initiated by ``assembly { ... }`` and inside
these curly braces, the following can be used (see the later sections for more details)
- literals, e.g. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
- opcodes in functional style, e.g. ``add(1, mload(0))``
- labels, e.g. ``name:``
- variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)``
- identifiers (externals, labels or assembly-local variables), e.g. ``jump(name)``, ``3 x add``
- assignments (in "instruction style"), e.g. ``3 =: x``
- assignments in functional style, e.g. ``x := add(y, 3)``
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
Opcodes
-------
This document does not want to be a full description of the Ethereum virtual machine, but the
following list can be used as a reference of its opcodes.
If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
Note that the order of arguments can be seen as being reversed compared to the instructional style (explained below).
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
special and all others push exactly one item onte the stack.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
+-------------------------+------+-----------------------------------------------------------------+
| stop + `-` | stop execution, identical to return(0,0) |
+-------------------------+------+-----------------------------------------------------------------+
| add(x, y) | | x + y |
+-------------------------+------+-----------------------------------------------------------------+
| sub(x, y) | | x - y |
+-------------------------+------+-----------------------------------------------------------------+
| mul(x, y) | | x * y |
+-------------------------+------+-----------------------------------------------------------------+
| div(x, y) | | x / y |
+-------------------------+------+-----------------------------------------------------------------+
| sdiv(x, y) | | x / y, for signed numbers in two's complement |
+-------------------------+------+-----------------------------------------------------------------+
| mod(x, y) | | x % y |
+-------------------------+------+-----------------------------------------------------------------+
| smod(x, y) | | x % y, for signed numbers in two's complement |
+-------------------------+------+-----------------------------------------------------------------+
| exp(x, y) | | x to the power of y |
+-------------------------+------+-----------------------------------------------------------------+
| not(x) | | ~x, every bit of x is negated |
+-------------------------+------+-----------------------------------------------------------------+
| lt(x, y) | | 1 if x < y, 0 otherwise |
+-------------------------+------+-----------------------------------------------------------------+
| gt(x, y) | | 1 if x > y, 0 otherwise |
+-------------------------+------+-----------------------------------------------------------------+
| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
+-------------------------+------+-----------------------------------------------------------------+
| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
+-------------------------+------+-----------------------------------------------------------------+
| eq(x, y) | | 1 if x == y, 0 otherwise |
+-------------------------+------+-----------------------------------------------------------------+
| iszero(x) | | 1 if x == 0, 0 otherwise |
+-------------------------+------+-----------------------------------------------------------------+
| and(x, y) | | bitwise and of x and y |
+-------------------------+------+-----------------------------------------------------------------+
| or(x, y) | | bitwise or of x and y |
+-------------------------+------+-----------------------------------------------------------------+
| xor(x, y) | | bitwise xor of x and y |
+-------------------------+------+-----------------------------------------------------------------+
| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte |
+-------------------------+------+-----------------------------------------------------------------+
| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics |
+-------------------------+------+-----------------------------------------------------------------+
| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics |
+-------------------------+------+-----------------------------------------------------------------+
| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant |
+-------------------------+------+-----------------------------------------------------------------+
| sha3(p, n) | | keccak(mem[p...(p+n))) |
+-------------------------+------+-----------------------------------------------------------------+
| jump(label) | `-` | jump to label / code position |
+-------------------------+------+-----------------------------------------------------------------+
| jumpi(label, cond) | `-` | jump to label if cond is nonzero |
+-------------------------+------+-----------------------------------------------------------------+
| pc | | current position in code |
+-------------------------+------+-----------------------------------------------------------------+
| pop(x) | `-` | remove the element pushed by x |
+-------------------------+------+-----------------------------------------------------------------+
| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) |
+-------------------------+------+-----------------------------------------------------------------+
| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it |
+-------------------------+------+-----------------------------------------------------------------+
| mload(p) | | mem[p..(p+32)) |
+-------------------------+------+-----------------------------------------------------------------+
| mstore(p, v) | `-` | mem[p..(p+32)) := v |
+-------------------------+------+-----------------------------------------------------------------+
| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte |
+-------------------------+------+-----------------------------------------------------------------+
| sload(p) | | storage[p] |
+-------------------------+------+-----------------------------------------------------------------+
| sstore(p, v) | `-` | storage[p] := v |
+-------------------------+------+-----------------------------------------------------------------+
| msize | | size of memory, i.e. largest accessed memory index |
+-------------------------+------+-----------------------------------------------------------------+
| gas | | gas still available to execution |
+-------------------------+------+-----------------------------------------------------------------+
| address | | address of the current contract / execution context |
+-------------------------+------+-----------------------------------------------------------------+
| balance(a) | | wei balance at address a |
+-------------------------+------+-----------------------------------------------------------------+
| caller | | call sender (excluding delegatecall) |
+-------------------------+------+-----------------------------------------------------------------+
| callvalue | | wei sent together with the current call |
+-------------------------+------+-----------------------------------------------------------------+
| calldataload(p) | | calldata starting from position p (32 bytes) |
+-------------------------+------+-----------------------------------------------------------------+
| calldatasize | | size of calldata in bytes |
+-------------------------+------+-----------------------------------------------------------------+
| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t |
+-------------------------+------+-----------------------------------------------------------------+
| codesize | | size of the code of the current contract / execution context |
+-------------------------+------+-----------------------------------------------------------------+
| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t |
+-------------------------+------+-----------------------------------------------------------------+
| extcodesize(a) | | size of the code at address a |
+-------------------------+------+-----------------------------------------------------------------+
| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a |
+-------------------------+------+-----------------------------------------------------------------+
| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
| | | and return the new address |
+-------------------------+------+-----------------------------------------------------------------+
| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
| insize, out, outsize) | | providing g gas and v wei and output area |
| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
| | | and 1 on success |
+-------------------------+------+-----------------------------------------------------------------+
| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay |
| insize, out, outsize) | | in the context of the current contract otherwise |
+-------------------------+------+-----------------------------------------------------------------+
| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` |
| insize, out, outsize) | | and ``callvalue`` |
+-------------------------+------+-----------------------------------------------------------------+
| return(p, s) | `-` | end execution, return data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a |
+-------------------------+------+-----------------------------------------------------------------+
| invalid | `-` | end execution with invalid instruction |
+-------------------------+------+-----------------------------------------------------------------+
| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) |
+-------------------------+------+-----------------------------------------------------------------+
| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
| t4) | | |
+-------------------------+------+-----------------------------------------------------------------+
| origin | | transaction sender |
+-------------------------+------+-----------------------------------------------------------------+
| gasprice | | gas price of the transaction |
+-------------------------+------+-----------------------------------------------------------------+
| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current |
+-------------------------+------+-----------------------------------------------------------------+
| coinbase | | current mining beneficiary |
+-------------------------+------+-----------------------------------------------------------------+
| timestamp | | timestamp of the current block in seconds since the epoch |
+-------------------------+------+-----------------------------------------------------------------+
| number | | current block number |
+-------------------------+------+-----------------------------------------------------------------+
| difficulty | | difficulty of the current block |
+-------------------------+------+-----------------------------------------------------------------+
| gaslimit | | block gas limit of the current block |
+-------------------------+------+-----------------------------------------------------------------+
Literals
--------
You can use integer constants by typing them in decimal or hexadecimal notation and an
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc".
Strings are stored left-aligned and cannot be longer than 32 bytes.
.. code::
assembly { 2 3 add "abc" and }
Functional Style
-----------------
You can type opcode after opcode in the same way they will end up in bytecode. For example
adding ``3`` to the contents in memory at position ``0x80`` would be
.. code::
3 0x80 mload add 0x80 mstore
As it is often hard to see what the actual arguments for certain opcodes are,
Solidity inline assembly also provides a "functional style" notation where the same code
would be written as follows
.. code::
mstore(0x80, add(mload(0x80), 3))
Functional style and instructional style can be mixed, but any opcode inside a
functional style expression has to return exactly one stack slot (most of the opcodes do).
Note that the order of arguments is reversed in functional-style as opposed to the instruction-style
way. If you use functional-style, the first argument will end up on the stack top.
Access to External Variables and Functions
------------------------------------------
Solidity variables and other identifiers can be accessed by simply using their name.
For storage and memory variables, this will push the address and not the value onto the
stack. Also note that non-struct and non-array storage variable addresses occupy two slots
on the stack: One for the address and one for the byte offset inside the storage slot.
In assignments (see below), we can even use local Solidity variables to assign to.
Functions external to inline assembly can also be accessed: The assembly will
push their entry label (with virtual function resolution applied). The calling semantics
in solidity are:
- the caller pushes return label, arg1, arg2, ..., argn
- the call returns with ret1, ret2, ..., retn
This feature is still a bit cumbersome to use, because the stack offset essentially
changes during the call, and thus references to local variables will be wrong.
It is planned that the stack height changes can be specified in inline assembly.
.. code::
pragma solidity ^0.4.0;
contract C {
uint b;
function f(uint x) returns (uint r) {
assembly {
b pop // remove the offset, we know it is zero
sload
x
mul
=: r // assign to return variable r
}
}
}
Labels
------
Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses
which can change easily. Solidity inline assembly provides labels to make the use of
jumps easier. The following code computes an element in the Fibonacci series.
.. code::
{
let n := calldataload(4)
let a := 1
let b := a
loop:
jumpi(loopend, eq(n, 0))
a add swap1
n := sub(n, 1)
jump(loop)
loopend:
mstore(0, a)
return(0, 0x20)
}
Please note that automatically accessing stack variables can only work if the
assembler knows the current stack height. This fails to work if the jump source
and target have different stack heights. It is still fine to use such jumps,
you should just not access any stack variables (even assembly variables) in that case.
Furthermore, the stack height analyser goes through the code opcode by opcode
(and not according to control flow), so in the following case, the assembler
will have a wrong impression about the stack height at label ``two``:
.. code::
{
jump(two)
one:
// Here the stack height is 1 (because we pushed 7),
// but the assembler thinks it is 0 because it reads
// from top to bottom.
// Accessing stack variables here will lead to errors.
jump(three)
two:
7 // push something onto the stack
jump(one)
three:
}
.. note::
``invalidJumpLabel`` is a pre-defined label. Jumping to this location will always
result in an invalid jump, effectively aborting execution of the code.
Declaring Assembly-Local Variables
----------------------------------
You can use the ``let`` keyword to declare variables that are only visible in
inline assembly and actually only in the current ``{...}``-block. What happens
is that the ``let`` instruction will create a new stack slot that is reserved
for the variable and automatically removed again when the end of the block
is reached. You need to provide an initial value for the variable which can
be just ``0``, but it can also be a complex functional-style expression.
.. code::
pragma solidity ^0.4.0;
contract C {
function f(uint x) returns (uint b) {
assembly {
let v := add(x, 1)
mstore(0x80, v)
{
let y := add(sload(v), 1)
b := y
} // y is "deallocated" here
b := add(b, v)
} // v is "deallocated" here
}
}
Assignments
-----------
Assignments are possible to assembly-local variables and to function-local
variables. Take care that when you assign to variables that point to
memory or storage, you will only change the pointer and not the data.
There are two kinds of assignments: Functional-style and instruction-style.
For functional-style assignments (``variable := value``), you need to provide a value in a
functional-style expression that results in exactly one stack value
and for instruction-style (``=: variable``), the value is just taken from the stack top.
For both ways, the colon points to the name of the variable.
.. code::
assembly {
let v := 0 // functional-style assignment as part of variable declaration
let g := add(v, 2)
sload(10)
=: v // instruction style assignment, puts the result of sload(10) into v
}
Things to Avoid
---------------
Inline assembly might have a quite high-level look, but it actually is extremely
low-level. The only thing the assembler does for you is re-arranging
functional-style opcodes, managing jump labels, counting stack height for
variable access and removing stack slots for assembly-local variables when the end
of their block is reached. Especially for those two last cases, it is important
to know that the assembler only counts stack height from top to bottom, not
necessarily following control flow. Furthermore, operations like swap will only
swap the contents of the stack but not the location of variables.
Conventions in Solidity
-----------------------
In contrast to EVM assembly, Solidity knows types which are narrower than 256 bits,
e.g. ``uint24``. In order to make them more efficient, most arithmetic operations just
treat them as 256-bit numbers and the higher-order bits are only cleaned at the
point where it is necessary, i.e. just shortly before they are written to memory
or before comparisons are performed. This means that if you access such a variable
from within inline assembly, you might have to manually clean the higher order bits
first.
Solidity manages memory in a very simple way: There is a "free memory pointer"
at position ``0x40`` in memory. If you want to allocate memory, just use the memory
from that point on and update the pointer accordingly.
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
first slot of the array and then only the array elements follow.
.. warning::
Statically-sized memory arrays do not have a length field, but it will be added soon
to allow better convertibility between statically- and dynamically-sized arrays, so
please do not rely on that.

View File

@ -641,7 +641,7 @@ Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic
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? 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 The automatic getter function for a public state variable of array type only returns
individual elements. If you want to return the complete array, you have to individual elements. If you want to return the complete array, you have to
manually write a function to do that. manually write a function to do that.
@ -660,16 +660,6 @@ 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. 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);``.
Note that the call to ``send`` may fail in certain conditions, such as if you have insufficient funds, so you should always check the return value.
``send`` returns ``true`` if the send was successful and ``false`` otherwise.
What does the following strange check do in the Custom Token contract? What does the following strange check do in the Custom Token contract?
====================================================================== ======================================================================

View File

@ -68,7 +68,8 @@ Continue = 'continue'
Break = 'break' Break = 'break'
Return = 'return' Expression? Return = 'return' Expression?
Throw = 'throw' Throw = 'throw'
VariableDefinition = VariableDeclaration ( '=' Expression )? VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )?
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
// Precedence by order (see github.com/ethereum/solidity/pull/732) // Precedence by order (see github.com/ethereum/solidity/pull/732)
Expression = Expression =

View File

@ -39,6 +39,9 @@ Available Solidity Integrations
* `Ethereum Studio <https://live.ether.camp/>`_ * `Ethereum Studio <https://live.ether.camp/>`_
Specialized web IDE that also provides shell access to a complete Ethereum environment. Specialized web IDE that also provides shell access to a complete Ethereum environment.
* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_
Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs)
* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ * `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_
Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler.
@ -130,6 +133,7 @@ Contents
solidity-by-example.rst solidity-by-example.rst
solidity-in-depth.rst solidity-in-depth.rst
security-considerations.rst security-considerations.rst
using-the-compiler.rst
style-guide.rst style-guide.rst
common-patterns.rst common-patterns.rst
contributing.rst contributing.rst

View File

@ -25,24 +25,25 @@ without connection to the Internet, you can go to
https://github.com/ethereum/browser-solidity/tree/gh-pages and https://github.com/ethereum/browser-solidity/tree/gh-pages and
download the .ZIP file as explained on that page. download the .ZIP file as explained on that page.
npm / Node.js npm / Node.js
============= =============
This is probably the most portable and most convenient way to install Solidity locally. 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 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 into JavaScript using Emscripten. It can be used in projects directly (such as Browser-Solidity).
package available. Please refer to the `solc-js <https://github.com/ethereum/solc-js>`_ repository for instructions.
To install it, simply use It also contains a commandline tool called `solcjs`, which can be installed via npm:
.. code:: bash .. code:: bash
npm install solc npm install -g solc
Details about the usage of the Node.js package can be found in the .. note::
`solc-js repository <https://github.com/ethereum/solc-js>`_.
The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`)
expecting the behaviour of `solc` will not work with `solcjs`.
Docker Docker
====== ======
@ -82,6 +83,12 @@ If you want to use the cutting edge developer version:
sudo apt-get update sudo apt-get update
sudo apt-get install solc sudo apt-get install solc
Arch Linux also has packages, albeit limited to the latest development version:
.. code:: bash
pacman -S solidity-git
Homebrew is missing pre-built bottles at the time of writing, Homebrew is missing pre-built bottles at the time of writing,
following a Jenkins to TravisCI migration, but Homebrew following a Jenkins to TravisCI migration, but Homebrew
should still work just fine as a means to build-from-source. should still work just fine as a means to build-from-source.
@ -95,6 +102,22 @@ We will re-add the pre-built bottles soon.
brew install solidity brew install solidity
brew linkapps solidity brew linkapps solidity
If you need a specific version of Solidity you can install a
Homebrew formula directly from Github.
View
`solidity.rb commits on Github <https://github.com/ethereum/homebrew-ethereum/commits/master/solidity.rb>`_.
Follow the history links until you have a raw file link of a
specific commit of ``solidity.rb``.
Install it using ``brew``:
.. code:: bash
brew unlink solidity
# Install 0.4.8
brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb
.. _building-from-source: .. _building-from-source:
@ -197,7 +220,14 @@ Building Solidity is quite similar on Linux, macOS and other Unices:
cd build cd build
cmake .. && make cmake .. && make
And even on Windows: or even easier:
.. code:: bash
#note: this will install binaries solc and soltest at usr/local/bin
./scripts/build.sh
And even for Windows:
.. code:: bash .. code:: bash

View File

@ -109,8 +109,7 @@ that does not allow any arithmetic operations. It is suitable for
storing addresses of contracts or keypairs belonging to external storing addresses of contracts or keypairs belonging to external
persons. The keyword ``public`` automatically generates a function that persons. The keyword ``public`` automatically generates a function that
allows you to access the current value of the state variable. allows you to access the current value of the state variable.
Without this keyword, other contracts have no way to access the 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:: The function will look something like this::
function minter() returns (address) { return minter; } function minter() returns (address) { return minter; }
@ -132,7 +131,7 @@ 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 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 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, 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 like this one. The getter function created by the ``public`` keyword
is a bit more complex in this case. It roughly looks like the is a bit more complex in this case. It roughly looks like the
following:: following::

View File

@ -137,31 +137,6 @@ Different types have different rules for cleaning up invalid values:
| | |will be thrown | | | |will be thrown |
+---------------+---------------+-------------------+ +---------------+---------------+-------------------+
*****************
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 .. index:: optimizer, common subexpression elimination, constant propagation
************************* *************************
@ -246,45 +221,6 @@ This means the following source mappings represent the same information:
``1:2:1;:9;2::2;;`` ``1:2:1;:9;2::2;;``
.. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker
.. _commandline-compiler:
******************************
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 ``context: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.
You can restrict remappings to only certain source files by prefixing a context.
The section on :ref:`import` provides more details on remappings.
If there are multiple matches due to remappings, the one with the longest common prefix is selected.
If your contracts use :ref:`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.
***************** *****************
Contract Metadata Contract Metadata
***************** *****************
@ -427,7 +363,7 @@ Tips and Tricks
* Use ``delete`` on arrays to delete all its elements. * 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! * 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. * Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` 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 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 your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``.
* Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` * Initialise storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
@ -499,7 +435,7 @@ The following is the order of precedence for operators, listed in order of evalu
| *16* | Comma operator | ``,`` | | *16* | Comma operator | ``,`` |
+------------+-------------------------------------+--------------------------------------------+ +------------+-------------------------------------+--------------------------------------------+
.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send .. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
Global Variables Global Variables
================ ================
@ -517,6 +453,9 @@ Global Variables
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
- ``tx.gasprice`` (``uint``): gas price of the transaction - ``tx.gasprice`` (``uint``): gas price of the transaction
- ``tx.origin`` (``address``): sender of the transaction (full call chain) - ``tx.origin`` (``address``): sender of the transaction (full call chain)
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input)
- ``revert()``: abort execution and revert state changes
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments - ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments
- ``sha3(...) returns (bytes32)``: an alias to `keccak256()` - ``sha3(...) returns (bytes32)``: an alias to `keccak256()`
- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments
@ -527,8 +466,9 @@ Global Variables
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
- ``super``: the contract one level higher in the inheritance hierarchy - ``super``: the contract one level higher in the inheritance hierarchy
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
- ``<address>.balance`` (``uint256``): balance of the address in Wei - ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to address, returns ``false`` on failure - ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
- ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
.. index:: visibility, public, private, external, internal .. index:: visibility, public, private, external, internal
@ -541,7 +481,7 @@ Function Visibility Specifiers
return true; return true;
} }
- ``public``: visible externally and internally (creates accessor function for storage/state variables) - ``public``: visible externally and internally (creates getter function for storage/state variables)
- ``private``: only visible in the current contract - ``private``: only visible in the current contract
- ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``) - ``external``: only visible externally (only for functions) - i.e. can only be message-called (via ``this.func``)
- ``internal``: only visible internally - ``internal``: only visible internally

View File

@ -106,6 +106,10 @@ of votes.
if (sender.voted) if (sender.voted)
throw; throw;
// Self-delegation is not allowed.
if (to == msg.sender)
throw;
// Forward the delegation as long as // Forward the delegation as long as
// `to` also delegated. // `to` also delegated.
// In general, such loops are very dangerous, // In general, such loops are very dangerous,
@ -114,16 +118,12 @@ of votes.
// In this case, the delegation will not be executed, // In this case, the delegation will not be executed,
// but in other situations, such loops might // but in other situations, such loops might
// cause a contract to get "stuck" completely. // cause a contract to get "stuck" completely.
while ( while (voters[to].delegate != address(0)) {
voters[to].delegate != address(0) &&
voters[to].delegate != msg.sender
) {
to = voters[to].delegate; to = voters[to].delegate;
}
// We found a loop in the delegation, not allowed. // We found a loop in the delegation, not allowed.
if (to == msg.sender) { if (to == msg.sender)
throw; throw;
} }
// Since `sender` is a reference, this // Since `sender` is a reference, this

View File

@ -28,7 +28,7 @@ State variables are values which are permanently stored in contract storage.
} }
See the :ref:`types` section for valid state variable types and See the :ref:`types` section for valid state variable types and
:ref:`visibility-and-accessors` for possible choices for :ref:`visibility-and-getters` for possible choices for
visibility. visibility.
.. _structure-functions: .. _structure-functions:
@ -49,7 +49,7 @@ Functions are the executable units of code within a contract.
} }
:ref:`function-calls` can happen internally or externally :ref:`function-calls` can happen internally or externally
and have different levels of visibility (:ref:`visibility-and-accessors`) and have different levels of visibility (:ref:`visibility-and-getters`)
towards other contracts. towards other contracts.
.. _structure-function-modifiers: .. _structure-function-modifiers:

View File

@ -64,7 +64,7 @@ expression ``x << y`` is equivalent to ``x * 2**y`` and ``x >> y`` is
equivalent to ``x / 2**y``. This means that shifting negative numbers equivalent to ``x / 2**y``. This means that shifting negative numbers
sign extends. Shifting by a negative amount throws a runtime exception. sign extends. Shifting by a negative amount throws a runtime exception.
.. index:: address, balance, send, call, callcode, delegatecall .. index:: address, balance, send, call, callcode, delegatecall, transfer
.. _address: .. _address:
@ -80,27 +80,31 @@ Operators:
Members of Addresses Members of Addresses
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
* ``balance`` and ``send`` * ``balance`` and ``transfer``
For a quick reference, see :ref:`address_related`. For a quick reference, see :ref:`address_related`.
It is possible to query the balance of an address using the property ``balance`` 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: and to send Ether (in units of wei) to an address using the ``transfer`` function:
:: ::
address x = 0x123; address x = 0x123;
address myAddress = this; address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.send(10); if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
.. note:: .. 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``. If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``transfer`` 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 and the current contract will stop with an exception.
* ``send``
Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``.
.. warning:: .. warning::
There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024
(this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order
to make safe Ether transfers, always check the return value of ``send`` or even better: to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better:
Use a pattern where the recipient withdraws the money. use a pattern where the recipient withdraws the money.
* ``call``, ``callcode`` and ``delegatecall`` * ``call``, ``callcode`` and ``delegatecall``
@ -193,12 +197,14 @@ Rational and Integer Literals
Integer literals are formed from a sequence of numbers in the range 0-9. Integer literals are formed from a sequence of numbers in the range 0-9.
They are interpreted as decimals. For example, ``69`` means sixty nine. They are interpreted as decimals. For example, ``69`` means sixty nine.
Octal literals do not exist in Solidity and leading zeros are ignored. Octal literals do not exist in Solidity and leading zeros are invalid.
For example, ``0100`` means one hundred.
Decimal literals are formed by a ``.`` with at least one number on Decimal fraction literals are formed by a ``.`` with at least one number on
one side. Examples include ``1.``, ``.1`` and ``1.3``. one side. Examples include ``1.``, ``.1`` and ``1.3``.
Scientific notation is also supported, where the base can have fractions, while the exponent cannot.
Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``.
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal expression). using them together with a non-literal expression).
This means that computations do not overflow and divisions do not truncate This means that computations do not overflow and divisions do not truncate
@ -543,8 +549,8 @@ So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper.
that you are accessing the low-level bytes of the UTF-8 representation, that you are accessing the low-level bytes of the UTF-8 representation,
and not the individual characters! and not the individual characters!
It is possible to mark arrays ``public`` and have Solidity create an accessor. It is possible to mark arrays ``public`` and have Solidity create a getter.
The numeric index will become a required parameter for the accessor. The numeric index will become a required parameter for the getter.
.. index:: ! array;allocating, new .. index:: ! array;allocating, new
@ -792,11 +798,11 @@ Because of this, mappings do not have a length or a concept of a key or value be
Mappings are only allowed for state variables (or as storage reference types Mappings are only allowed for state variables (or as storage reference types
in internal functions). in internal functions).
It is possible to mark mappings ``public`` and have Solidity create an accessor. It is possible to mark mappings ``public`` and have Solidity create a getter.
The ``_KeyType`` will become a required parameter for the accessor and it will The ``_KeyType`` will become a required parameter for the getter and it will
return ``_ValueType``. return ``_ValueType``.
The ``_ValueType`` can be a mapping too. The accessor will have one parameter The ``_ValueType`` can be a mapping too. The getter will have one parameter
for each ``_KeyType``, recursively. for each ``_KeyType``, recursively.
:: ::

View File

@ -79,11 +79,13 @@ Block and Transaction Properties
You can only access the hashes of the most recent 256 blocks, all other You can only access the hashes of the most recent 256 blocks, all other
values will be zero. values will be zero.
.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send .. index:: assert, revert, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send
Mathematical and Cryptographic Functions Mathematical and Cryptographic Functions
---------------------------------------- ----------------------------------------
``assert(bool condition)``:
throws if the condition is not met.
``addmod(uint x, uint y, uint k) returns (uint)``: ``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``. 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)``: ``mulmod(uint x, uint y, uint k) returns (uint)``:
@ -98,6 +100,8 @@ Mathematical and Cryptographic Functions
compute RIPEMD-160 hash of the (tightly packed) arguments compute RIPEMD-160 hash of the (tightly packed) arguments
``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``:
recover the address associated with the public key from elliptic curve signature or return zero on error recover the address associated with the public key from elliptic curve signature or return zero on error
``revert()``:
abort execution and revert state changes
In the above, "tightly packed" means that the arguments are concatenated without padding. In the above, "tightly packed" means that the arguments are concatenated without padding.
This means that the following are all identical:: This means that the following are all identical::
@ -126,6 +130,8 @@ Address Related
balance of the :ref:`address` in Wei balance of the :ref:`address` in Wei
``<address>.send(uint256 amount) returns (bool)``: ``<address>.send(uint256 amount) returns (bool)``:
send given amount of Wei to :ref:`address`, returns ``false`` on failure send given amount of Wei to :ref:`address`, returns ``false`` on failure
``<address>.transfer(uint256 amount)``:
send given amount of Wei to :ref:`address`, throws on failure
For more information, see the section on :ref:`address`. For more information, see the section on :ref:`address`.

266
docs/using-the-compiler.rst Normal file
View File

@ -0,0 +1,266 @@
******************
Using the compiler
******************
.. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker
.. _commandline-compiler:
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 :ref:`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.
.. _compiler-api:
Compiler Input and Output JSON Description
******************************************
.. warning::
This JSON interface is not yet supported by the Solidity compiler, but will be released in a future version.
These JSON formats are used by the compiler API as well as are available through ``solc``. These are subject to change,
some fields are optional (as noted), but it is aimed at to only make backwards compatible changes.
The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output.
Comments are of course not permitted and used here only for explanatory purposes.
Input Description
-----------------
.. code-block:: none
{
// Required: Source code language, such as "Solidity", "serpent", "lll", "assembly", etc.
language: "Solidity",
// Required
sources:
{
// The keys here are the "global" names of the source files,
// imports can use other files via remappings (see below).
"myFile.sol":
{
// Optional: keccak256 hash of the source file
// It is used to verify the retrieved content if imported via URLs.
"keccak256": "0x123...",
// Required (unless "content" is used, see below): URL(s) to the source file.
// URL(s) should be imported in this order and the result checked against the
// keccak256 hash (if available). If the hash doesn't match or none of the
// URL(s) result in success, an error should be raised.
"urls":
[
"bzzr://56ab...",
"ipfs://Qma...",
"file:///tmp/path/to/file.sol"
]
},
"mortal":
{
// Optional: keccak256 hash of the source file
"keccak256": "0x234...",
// Required (unless "urls" is used): literal contents of the source file
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
}
},
// Optional
settings:
{
// Optional: Sorted list of remappings
remappings: [ ":g/dir" ],
// Optional: Optimizer settings (enabled defaults to false)
optimizer: {
enabled: true,
runs: 500
},
// Metadata settings (optional)
metadata: {
// Use only literal content and not URLs (false by default)
useLiteralContent: true
},
// Addresses of the libraries. If not all libraries are given here, it can result in unlinked objects whose output data is different.
libraries: {
// The top level key is the the name of the source file where the library is used.
// If remappings are used, this source file should match the global path after remappings were applied.
// If this key is an empty string, that refers to a global level.
"myFile.sol": {
"MyLib": "0x123123..."
}
}
// The following can be used to select desired outputs.
// If this field is omitted, then the compiler loads and does type checking, but will not generate any outputs apart from errors.
// The first level key is the file name and the second is the contract name, where empty contract name refers to the file itself,
// while the star refers to all of the contracts.
//
// The available output types are as follows:
// abi - ABI
// ast - AST of all source files
// why3 - Why3 translated output
// devdoc - Developer documentation (natspec)
// userdoc - User documentation (natspec)
// metadata - Metadata
// evm.ir - New assembly format before desugaring
// evm.assembly - New assembly format after desugaring
// evm.legacyAssemblyJSON - Old-style assembly format in JSON
// evm.opcodes - Opcodes list
// evm.methodIdentifiers - The list of function hashes
// evm.gasEstimates - Function gas estimates
// evm.bytecode - Bytecode
// evm.deployedBytecode - Deployed bytecode
// evm.sourceMap - Source mapping (useful for debugging)
// ewasm.wast - eWASM S-expressions format (not supported atm)
// ewasm.wasm - eWASM binary format (not supported atm)
outputSelection: {
// Enable the metadata and bytecode outputs of every single contract.
"*": {
"*": [ "metadata", "evm.bytecode" ]
},
// Enable the abi and opcodes output of MyContract defined in file def.
"def": {
"MyContract": [ "abi", "evm.opcodes" ]
},
// Enable the source map output of every single contract.
"*": {
"*": [ "evm.sourceMap" ]
},
// Enable the AST and Why3 output of every single file.
"*": {
"": [ "ast", "why3" ]
}
}
}
}
Output Description
------------------
.. code-block:: none
{
// Optional: not present if no errors/warnings were encountered
errors: [
{
// Optional: Location within the source file.
sourceLocation: {
file: "sourceFile.sol",
start: 0,
end: 100
],
// Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc
type: "TypeError",
// Mandatory: Component where the error originated, such as "general", "why3", "ewasm", etc.
component: "general",
// Mandatory ("error" or "warning")
severity: "error",
// Mandatory
message: "Invalid keyword"
}
],
// This contains the file-level outputs. In can be limited/filtered by the outputSelection settings.
sources: {
"sourceFile.sol": {
// Identifier (used in source maps)
id: 1,
// The AST object
ast: {}
}
},
// This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings.
contracts: {
"sourceFile.sol": {
// If the language used has no contract names, this field should equal to an empty string.
"ContractName": {
// The Ethereum Contract ABI. If empty, it is represented as an empty array.
// See https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
abi: [],
evm: {
// Intermediate representation (string)
ir: "",
// Assembly (string)
assembly: "",
// Old-style assembly (string)
legacyAssemblyJSON: [],
// Bytecode and related details.
bytecode: {
// The bytecode as a hex string.
object: "00fe",
// The source mapping as a string. See the source mapping definition.
sourceMap: "",
// If given, this is an unlinked object.
linkReferences: {
"libraryFile.sol": {
// Byte offsets into the bytecode. Linking replaces the 20 bytes located there.
"Library1": [
{ start: 0, length: 20 },
{ start: 200, length: 20 }
]
}
}
}
// The same layout as above.
deployedBytecode: { },
// Opcodes list (string)
opcodes: "",
// The list of function hashes
methodIdentifiers: {
"5c19a95c": "delegate(address)",
},
// Function gas estimates
gasEstimates: {
creation: {
dataCost: 420000,
// -1 means infinite (aka. unknown)
executionCost: -1
},
external: {
"delegate(address)": 25000
},
internal: {
"heavyLifting()": -1
}
}
},
// See the Metadata Output documentation
metadata: {},
ewasm: {
// S-expressions format
wast: "",
// Binary format (hex string)
wasm: ""
},
// User documentation (natspec)
userdoc: {},
// Developer documentation (natspec)
devdoc: {}
}
}
},
// Why3 output (string)
why3: ""
}

View File

@ -54,11 +54,10 @@ class SolidityLexer(RegexLexer):
r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'), r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'),
(r'[{(\[;,]', Punctuation, 'slashstartsregex'), (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
(r'[})\].]', Punctuation), (r'[})\].]', Punctuation),
(r'(for|in|while|do|break|return|continue|switch|case|default|if|else|' (r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|'
r'throw|try|catch|finally|new|delete|typeof|instanceof|void|' r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|'
r'this|import|mapping|returns|private|public|external|internal|' r'private|return|returns|storage|super|this|throw|using|while)\b', Keyword, 'slashstartsregex'),
r'constant|memory|storage|payable)\b', Keyword, 'slashstartsregex'), (r'(var|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'),
(r'(var|let|with|function|event|modifier|struct|enum|contract|library)\b', Keyword.Declaration, 'slashstartsregex'),
(r'(bytes|string|address|uint|int|bool|byte|' + (r'(bytes|string|address|uint|int|bool|byte|' +
'|'.join( '|'.join(
['uint%d' % (i + 8) for i in range(0, 256, 8)] + ['uint%d' % (i + 8) for i in range(0, 256, 8)] +
@ -68,16 +67,11 @@ class SolidityLexer(RegexLexer):
['fixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] ['fixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)]
) + r')\b', Keyword.Type, 'slashstartsregex'), ) + r')\b', Keyword.Type, 'slashstartsregex'),
(r'(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\b', Keyword.Type, 'slashstartsregex'), (r'(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\b', Keyword.Type, 'slashstartsregex'),
(r'(abstract|boolean|byte|char|class|const|debugger|double|enum|export|' (r'(abstract|after|case|catch|default|final|in|inline|interface|let|match|'
r'extends|final|float|goto|implements|int|interface|long|native|' r'null|of|pure|relocatable|static|switch|try|type|typeof|view)\b', Keyword.Reserved),
r'package|private|protected|public|short|static|super|synchronized|throws|' (r'(true|false)\b', Keyword.Constant),
r'transient|volatile)\b', Keyword.Reserved), (r'(block|msg|tx|now|suicide|selfdestruct|addmod|mulmod|sha3|keccak256|log[0-4]|'
(r'(true|false|null|NaN|Infinity|undefined)\b', Keyword.Constant), r'sha256|ecrecover|ripemd160|assert|revert)', Name.Builtin),
(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'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other),
(r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
(r'0x[0-9a-fA-F]+', Number.Hex), (r'0x[0-9a-fA-F]+', Number.Hex),

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ABI.h /** @file ABI.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file Assertions.h * @file Assertions.h

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Common.h /** @file Common.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CommonData.cpp /** @file CommonData.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CommonData.h /** @file CommonData.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CommonIO.cpp /** @file CommonIO.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CommonIO.h /** @file CommonIO.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Exceptions.h /** @file Exceptions.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
@ -41,6 +41,9 @@ struct Exception: virtual std::exception, virtual boost::exception
Exception(std::string _message = std::string()): m_message(std::move(_message)) {} Exception(std::string _message = std::string()): m_message(std::move(_message)) {}
const char* what() const noexcept override { return m_message.empty() ? std::exception::what() : m_message.c_str(); } const char* what() const noexcept override { return m_message.empty() ? std::exception::what() : m_message.c_str(); }
/// @returns "FileName:LineNumber" referring to the point where the exception was thrown.
std::string lineInfo() const;
private: private:
std::string m_message; std::string m_message;
}; };

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file FixedHash.h /** @file FixedHash.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file JSON.h /** @file JSON.h
* @date 2016 * @date 2016

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file SHA3.cpp /** @file SHA3.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file SHA3.h /** @file SHA3.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file SwarmHash.cpp /** @file SwarmHash.cpp
*/ */

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file SwarmHash.h /** @file SwarmHash.h
*/ */

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file UTF8.cpp /** @file UTF8.cpp
* @author Alex Beregszaszi * @author Alex Beregszaszi

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file UTF8.h /** @file UTF8.h
* @author Alex Beregszaszi * @author Alex Beregszaszi

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file UndefMacros.h /** @file UndefMacros.h
* @author Lefteris <lefteris@ethdev.com> * @author Lefteris <lefteris@ethdev.com>

View File

@ -130,8 +130,8 @@ public:
if (!_item.location().isEmpty() && _item.location() != m_location) if (!_item.location().isEmpty() && _item.location() != m_location)
{ {
flush(); flush();
printLocation();
m_location = _item.location(); m_location = _item.location();
printLocation();
} }
if (!( if (!(
_item.canBeFunctional() && _item.canBeFunctional() &&

View File

@ -194,7 +194,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
// Is not always better, try literal and decomposition method. // Is not always better, try literal and decomposition method.
AssemblyItems routine{u256(_value)}; AssemblyItems routine{u256(_value)};
bigint bestGas = gasNeeded(routine); bigint bestGas = gasNeeded(routine);
for (unsigned bits = 255; bits > 8; --bits) for (unsigned bits = 255; bits > 8 && m_maxSteps > 0; --bits)
{ {
unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff; unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff;
if (gapDetector != 0xff && gapDetector != 0x100) if (gapDetector != 0xff && gapDetector != 0x100)
@ -219,6 +219,8 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
else if (lowerPart < 0) else if (lowerPart < 0)
newRoutine.push_back(Instruction::SUB); newRoutine.push_back(Instruction::SUB);
if (m_maxSteps > 0)
m_maxSteps--;
bigint newGas = gasNeeded(newRoutine); bigint newGas = gasNeeded(newRoutine);
if (newGas < bestGas) if (newGas < bestGas)
{ {

View File

@ -143,6 +143,8 @@ protected:
AssemblyItems findRepresentation(u256 const& _value); AssemblyItems findRepresentation(u256 const& _value);
bigint gasNeeded(AssemblyItems const& _routine); bigint gasNeeded(AssemblyItems const& _routine);
/// Counter for the complexity of optimization, will stop when it reaches zero.
size_t m_maxSteps = 10000;
AssemblyItems m_routine; AssemblyItems m_routine;
}; };

View File

@ -47,7 +47,7 @@ struct EVMSchedule
unsigned callStipend = 2300; unsigned callStipend = 2300;
unsigned callValueTransferGas = 9000; unsigned callValueTransferGas = 9000;
unsigned callNewAccountGas = 25000; unsigned callNewAccountGas = 25000;
unsigned suicideRefundGas = 24000; unsigned selfdestructRefundGas = 24000;
unsigned memoryGas = 3; unsigned memoryGas = 3;
unsigned quadCoeffDiv = 512; unsigned quadCoeffDiv = 512;
unsigned createDataGas = 200; unsigned createDataGas = 200;

View File

@ -80,6 +80,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
gas += GasCosts::sloadGas; gas += GasCosts::sloadGas;
break; break;
case Instruction::RETURN: case Instruction::RETURN:
case Instruction::REVERT:
gas += memoryGas(0, -1); gas += memoryGas(0, -1);
break; break;
case Instruction::MLOAD: case Instruction::MLOAD:

View File

@ -61,7 +61,7 @@ namespace GasCosts
static unsigned const callStipend = 2300; static unsigned const callStipend = 2300;
static unsigned const callValueTransferGas = 9000; static unsigned const callValueTransferGas = 9000;
static unsigned const callNewAccountGas = 25000; static unsigned const callNewAccountGas = 25000;
static unsigned const suicideRefundGas = 24000; static unsigned const selfdestructRefundGas = 24000;
static unsigned const memoryGas = 3; static unsigned const memoryGas = 3;
static unsigned const quadCoeffDiv = 512; static unsigned const quadCoeffDiv = 512;
static unsigned const createDataGas = 200; static unsigned const createDataGas = 200;

View File

@ -159,8 +159,9 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "CALLCODE", Instruction::CALLCODE }, { "CALLCODE", Instruction::CALLCODE },
{ "RETURN", Instruction::RETURN }, { "RETURN", Instruction::RETURN },
{ "DELEGATECALL", Instruction::DELEGATECALL }, { "DELEGATECALL", Instruction::DELEGATECALL },
{ "REVERT", Instruction::REVERT },
{ "INVALID", Instruction::INVALID }, { "INVALID", Instruction::INVALID },
{ "SUICIDE", Instruction::SUICIDE } { "SELFDESTRUCT", Instruction::SELFDESTRUCT }
}; };
static const std::map<Instruction, InstructionInfo> c_instructionInfo = static const std::map<Instruction, InstructionInfo> c_instructionInfo =
@ -293,9 +294,10 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } },
{ Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } }, { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } },
{ Instruction::DELEGATECALL,{ "DELEGATECALL", 0, 6, 1, true, Tier::Special } }, { Instruction::DELEGATECALL, { "DELEGATECALL", 0, 6, 1, true, Tier::Special } },
{ Instruction::REVERT, { "REVERT", 0, 2, 0, true, Tier::Zero } },
{ Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } }, { Instruction::INVALID, { "INVALID", 0, 0, 0, true, Tier::Zero } },
{ Instruction::SUICIDE, { "SUICIDE", 0, 1, 0, true, Tier::Zero } } { Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Zero } }
}; };
void dev::solidity::eachInstruction( void dev::solidity::eachInstruction(

View File

@ -177,8 +177,9 @@ enum class Instruction: uint8_t
RETURN, ///< halt execution returning output data RETURN, ///< halt execution returning output data
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
REVERT = 0xfd, ///< halt execution, revert state and return output data
INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero)
SUICIDE = 0xff ///< halt execution and register account for later deletion SELFDESTRUCT = 0xff ///< halt execution and register account for later deletion
}; };
/// @returns the number of PUSH Instruction _inst /// @returns the number of PUSH Instruction _inst
@ -202,28 +203,28 @@ inline unsigned getSwapNumber(Instruction _inst)
/// @returns the PUSH<_number> instruction /// @returns the PUSH<_number> instruction
inline Instruction pushInstruction(unsigned _number) inline Instruction pushInstruction(unsigned _number)
{ {
assertThrow(1 <= _number && _number <= 32, InvalidOpcode, "Invalid PUSH instruction requested."); assertThrow(1 <= _number && _number <= 32, InvalidOpcode, std::string("Invalid PUSH instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::PUSH1) + _number - 1); return Instruction(unsigned(Instruction::PUSH1) + _number - 1);
} }
/// @returns the DUP<_number> instruction /// @returns the DUP<_number> instruction
inline Instruction dupInstruction(unsigned _number) inline Instruction dupInstruction(unsigned _number)
{ {
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, "Invalid DUP instruction requested."); assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid DUP instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::DUP1) + _number - 1); return Instruction(unsigned(Instruction::DUP1) + _number - 1);
} }
/// @returns the SWAP<_number> instruction /// @returns the SWAP<_number> instruction
inline Instruction swapInstruction(unsigned _number) inline Instruction swapInstruction(unsigned _number)
{ {
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, "Invalid SWAP instruction requested."); assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid SWAP instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::SWAP1) + _number - 1); return Instruction(unsigned(Instruction::SWAP1) + _number - 1);
} }
/// @returns the LOG<_number> instruction /// @returns the LOG<_number> instruction
inline Instruction logInstruction(unsigned _number) inline Instruction logInstruction(unsigned _number)
{ {
assertThrow(_number <= 4, InvalidOpcode, "Invalid LOG instruction requested."); assertThrow(_number <= 4, InvalidOpcode, std::string("Invalid LOG instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::LOG0) + _number); return Instruction(unsigned(Instruction::LOG0) + _number);
} }

View File

@ -200,7 +200,8 @@ struct UnreachableCode
it[0] != Instruction::RETURN && it[0] != Instruction::RETURN &&
it[0] != Instruction::STOP && it[0] != Instruction::STOP &&
it[0] != Instruction::INVALID && it[0] != Instruction::INVALID &&
it[0] != Instruction::SUICIDE it[0] != Instruction::SELFDESTRUCT &&
it[0] != Instruction::REVERT
) )
return false; return false;

View File

@ -116,9 +116,10 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
case Instruction::JUMP: case Instruction::JUMP:
case Instruction::JUMPI: case Instruction::JUMPI:
case Instruction::RETURN: case Instruction::RETURN:
case Instruction::SUICIDE: case Instruction::SELFDESTRUCT:
case Instruction::STOP: case Instruction::STOP:
case Instruction::INVALID: case Instruction::INVALID:
case Instruction::REVERT:
return true; return true;
default: default:
return false; return false;

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CodeFragment.cpp /** @file CodeFragment.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CodeFragment.h /** @file CodeFragment.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Compiler.cpp /** @file Compiler.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Compiler.h /** @file Compiler.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CompilerState.cpp /** @file CompilerState.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file CompilerState.h /** @file CompilerState.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Exceptions.h /** @file Exceptions.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Parser.cpp /** @file Parser.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Parser.h /** @file Parser.h
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -65,7 +65,13 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160", make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true)),
make_shared<MagicVariableDeclaration>("assert",
make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Assert)),
make_shared<MagicVariableDeclaration>("require",
make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Location::Require)),
make_shared<MagicVariableDeclaration>("revert",
make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Revert))})
{ {
} }

View File

@ -21,6 +21,7 @@
*/ */
#include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
@ -34,8 +35,10 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver( NameAndTypeResolver::NameAndTypeResolver(
vector<Declaration const*> const& _globals, vector<Declaration const*> const& _globals,
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ErrorList& _errors ErrorList& _errors
) : ) :
m_scopes(_scopes),
m_errors(_errors) m_errors(_errors)
{ {
if (!m_scopes[nullptr]) if (!m_scopes[nullptr])
@ -44,18 +47,12 @@ NameAndTypeResolver::NameAndTypeResolver(
m_scopes[nullptr]->registerDeclaration(*declaration); m_scopes[nullptr]->registerDeclaration(*declaration);
} }
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope)
{ {
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. // The helper registers all declarations in m_scopes as a side-effect of its construction.
try try
{ {
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors); DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors, _currentScope);
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
} }
catch (FatalError const&) catch (FatalError const&)
{ {
@ -132,68 +129,11 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
return !error; return !error;
} }
bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
{ {
try try
{ {
m_currentScope = m_scopes[_contract.scope()].get(); return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
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&) catch (FatalError const&)
{ {
@ -201,7 +141,6 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
throw; // Something is weird here, rather throw again. throw; // Something is weird here, rather throw again.
return false; return false;
} }
return true;
} }
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
@ -257,21 +196,25 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
solAssert(_declarations.size() > 1, ""); solAssert(_declarations.size() > 1, "");
vector<Declaration const*> uniqueFunctions; vector<Declaration const*> uniqueFunctions;
for (auto it = _declarations.begin(); it != _declarations.end(); ++it) for (Declaration const* declaration: _declarations)
{ {
solAssert(*it, ""); solAssert(declaration, "");
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1 // the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
solAssert(dynamic_cast<FunctionDefinition const*>(*it) || dynamic_cast<EventDefinition const*>(*it) || dynamic_cast<VariableDeclaration const*>(*it), solAssert(
"Found overloading involving something not a function or a variable"); dynamic_cast<FunctionDefinition const*>(declaration) ||
dynamic_cast<EventDefinition const*>(declaration) ||
dynamic_cast<VariableDeclaration const*>(declaration),
"Found overloading involving something not a function or a variable."
);
shared_ptr<FunctionType const> functionType { (*it)->functionType(false) }; FunctionTypePointer functionType { declaration->functionType(false) };
if (!functionType) if (!functionType)
functionType = (*it)->functionType(true); functionType = declaration->functionType(true);
solAssert(functionType, "failed to determine the function type of the overloaded"); solAssert(functionType, "Failed to determine the function type of the overloaded.");
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes()) for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter) if (!parameter)
reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context"); reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
if (uniqueFunctions.end() == find_if( if (uniqueFunctions.end() == find_if(
uniqueFunctions.begin(), uniqueFunctions.begin(),
@ -284,11 +227,73 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType); return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType);
} }
)) ))
uniqueFunctions.push_back(*it); uniqueFunctions.push_back(declaration);
} }
return uniqueFunctions; return uniqueFunctions;
} }
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
{
bool success = true;
m_currentScope = m_scopes[contract->scope()].get();
solAssert(!!m_currentScope, "");
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
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[contract].get();
if (!resolveNamesAndTypes(*node, false))
{
success = false;
break;
}
}
if (!success)
return false;
if (!_resolveInsideCode)
return success;
m_currentScope = m_scopes[contract].get();
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
m_currentScope = m_scopes[contract].get();
if (!resolveNamesAndTypes(*node, true))
success = false;
}
return success;
}
else
{
if (m_scopes.count(&_node))
m_currentScope = m_scopes[&_node].get();
return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
}
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{ {
auto iterator = m_scopes.find(&_base); auto iterator = m_scopes.find(&_base);
@ -459,14 +464,30 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
DeclarationRegistrationHelper::DeclarationRegistrationHelper( DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot, ASTNode& _astRoot,
ErrorList& _errors ErrorList& _errors,
ASTNode const* _currentScope
): ):
m_scopes(_scopes), m_scopes(_scopes),
m_currentScope(&_astRoot), m_currentScope(_currentScope),
m_errors(_errors) m_errors(_errors)
{ {
solAssert(!!m_scopes.at(m_currentScope), "");
_astRoot.accept(*this); _astRoot.accept(*this);
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
}
bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
{
if (!m_scopes[&_sourceUnit])
// By importing, it is possible that the container already exists.
m_scopes[&_sourceUnit].reset(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
m_currentScope = &_sourceUnit;
return true;
}
void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit)
{
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
closeCurrentScope();
} }
bool DeclarationRegistrationHelper::visit(ImportDirective& _import) bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
@ -587,12 +608,13 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara
void DeclarationRegistrationHelper::closeCurrentScope() void DeclarationRegistrationHelper::closeCurrentScope()
{ {
solAssert(m_currentScope, "Closed non-existing scope."); solAssert(m_currentScope && m_scopes.count(m_currentScope), "Closed non-existing scope.");
m_currentScope = m_scopes[m_currentScope]->enclosingNode(); m_currentScope = m_scopes[m_currentScope]->enclosingNode();
} }
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{ {
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract())) if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
{ {
SourceLocation firstDeclarationLocation; SourceLocation firstDeclarationLocation;

View File

@ -42,15 +42,27 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable class NameAndTypeResolver: private boost::noncopyable
{ {
public: public:
NameAndTypeResolver(std::vector<Declaration const*> const& _globals, ErrorList& _errors); /// Creates the resolver with the given declarations added to the global scope.
/// Registers all declarations found in the source unit. /// @param _scopes mapping of scopes to be used (usually default constructed), these
/// are filled during the lifetime of this object.
NameAndTypeResolver(
std::vector<Declaration const*> const& _globals,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ErrorList& _errors
);
/// Registers all declarations found in the AST node, usually a source unit.
/// @returns false in case of error. /// @returns false in case of error.
bool registerDeclarations(SourceUnit& _sourceUnit); /// @param _currentScope should be nullptr but can be used to inject new declarations into
/// existing scopes, used by the snippets feature.
bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr);
/// Applies the effect of import directives. /// Applies the effect of import directives.
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits); bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
/// Resolves all names and types referenced from the given contract. /// Resolves all names and types referenced from the given AST Node.
/// This is usually only called at the contract level, but with a bit of care, it can also
/// be called at deeper levels.
/// @param _resolveInsideCode if false, does not descend into nodes that contain code.
/// @returns false in case of error. /// @returns false in case of error.
bool resolveNamesAndTypes(ContractDefinition& _contract); bool resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode = true);
/// Updates the given global declaration (used for "this"). Not to be used with declarations /// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope. /// that create their own scope.
/// @returns false in case of error. /// @returns false in case of error.
@ -77,7 +89,8 @@ public:
); );
private: private:
void reset(); /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
/// Imports all members declared directly in the given contract (i.e. does not import inherited members) /// 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. /// into the current scope if they are not present already.
@ -112,7 +125,7 @@ private:
/// where nullptr denotes the global scope. Note that structs are not scope since they do /// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code. /// not contain code.
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
DeclarationContainer* m_currentScope = nullptr; DeclarationContainer* m_currentScope = nullptr;
ErrorList& m_errors; ErrorList& m_errors;
@ -125,13 +138,20 @@ private:
class DeclarationRegistrationHelper: private ASTVisitor class DeclarationRegistrationHelper: private ASTVisitor
{ {
public: public:
/// Registers declarations in their scopes and creates new scopes as a side-effect
/// of construction.
/// @param _currentScope should be nullptr if we start at SourceUnit, but can be different
/// to inject new declarations into an existing scope, used by snippets.
DeclarationRegistrationHelper( DeclarationRegistrationHelper(
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot, ASTNode& _astRoot,
ErrorList& _errors ErrorList& _errors,
ASTNode const* _currentScope = nullptr
); );
private: private:
bool visit(SourceUnit& _sourceUnit) override;
void endVisit(SourceUnit& _sourceUnit) override;
bool visit(ImportDirective& _declaration) override; bool visit(ImportDirective& _declaration) override;
bool visit(ContractDefinition& _contract) override; bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override;

View File

@ -0,0 +1,108 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/Version.h>
#include <boost/range/adaptor/map.hpp>
#include <memory>
using namespace std;
using namespace dev;
using namespace dev::solidity;
bool PostTypeChecker::check(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
return Error::containsOnlyWarnings(m_errors);
}
void PostTypeChecker::typeError(SourceLocation const& _location, std::string const& _description)
{
auto err = make_shared<Error>(Error::Type::TypeError);
*err <<
errinfo_sourceLocation(_location) <<
errinfo_comment(_description);
m_errors.push_back(err);
}
bool PostTypeChecker::visit(ContractDefinition const&)
{
solAssert(!m_currentConstVariable, "");
solAssert(m_constVariableDependencies.empty(), "");
return true;
}
void PostTypeChecker::endVisit(ContractDefinition const&)
{
solAssert(!m_currentConstVariable, "");
for (auto declaration: m_constVariables)
if (auto identifier = findCycle(declaration))
typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + ".");
}
bool PostTypeChecker::visit(VariableDeclaration const& _variable)
{
solAssert(!m_currentConstVariable, "");
if (_variable.isConstant())
{
m_currentConstVariable = &_variable;
m_constVariables.push_back(&_variable);
}
return true;
}
void PostTypeChecker::endVisit(VariableDeclaration const& _variable)
{
if (_variable.isConstant())
{
solAssert(m_currentConstVariable == &_variable, "");
m_currentConstVariable = nullptr;
}
}
bool PostTypeChecker::visit(Identifier const& _identifier)
{
if (m_currentConstVariable)
if (auto var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
if (var->isConstant())
m_constVariableDependencies[m_currentConstVariable].insert(var);
return true;
}
VariableDeclaration const* PostTypeChecker::findCycle(
VariableDeclaration const* _startingFrom,
set<VariableDeclaration const*> const& _seen
)
{
if (_seen.count(_startingFrom))
return _startingFrom;
else if (m_constVariableDependencies.count(_startingFrom))
{
set<VariableDeclaration const*> seen(_seen);
seen.insert(_startingFrom);
for (auto v: m_constVariableDependencies[_startingFrom])
if (findCycle(v, seen))
return v;
}
return nullptr;
}

View File

@ -0,0 +1,69 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <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
{
/**
* This module performs analyses on the AST that are done after type checking and assignments of types:
* - whether there are circular references in constant state variables
* @TODO factor out each use-case into an individual class (but do the traversal only once)
*/
class PostTypeChecker: private ASTConstVisitor
{
public:
/// @param _errors the reference to the list of errors and warnings to add them found during type checking.
PostTypeChecker(ErrorList& _errors): m_errors(_errors) {}
bool check(ASTNode const& _astRoot);
private:
/// Adds a new error to the list of errors.
void typeError(SourceLocation const& _location, std::string const& _description);
virtual bool visit(ContractDefinition const& _contract) override;
virtual void endVisit(ContractDefinition const& _contract) override;
virtual bool visit(VariableDeclaration const& _declaration) override;
virtual void endVisit(VariableDeclaration const& _declaration) override;
virtual bool visit(Identifier const& _identifier) override;
VariableDeclaration const* findCycle(
VariableDeclaration const* _startingFrom,
std::set<VariableDeclaration const*> const& _seen = {}
);
ErrorList& m_errors;
VariableDeclaration const* m_currentConstVariable = nullptr;
std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism.
std::map<VariableDeclaration const*, std::set<VariableDeclaration const*>> m_constVariableDependencies;
};
}
}

View File

@ -35,14 +35,7 @@ using namespace dev::solidity;
bool ReferencesResolver::resolve(ASTNode const& _root) bool ReferencesResolver::resolve(ASTNode const& _root)
{ {
try _root.accept(*this);
{
_root.accept(*this);
}
catch (FatalError const&)
{
solAssert(m_errorOccurred, "");
}
return !m_errorOccurred; return !m_errorOccurred;
} }
@ -65,6 +58,30 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
return true; return true;
} }
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
{
m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
return true;
}
void ReferencesResolver::endVisit(FunctionDefinition const&)
{
solAssert(!m_returnParameters.empty(), "");
m_returnParameters.pop_back();
}
bool ReferencesResolver::visit(ModifierDefinition const&)
{
m_returnParameters.push_back(nullptr);
return true;
}
void ReferencesResolver::endVisit(ModifierDefinition const&)
{
solAssert(!m_returnParameters.empty(), "");
m_returnParameters.pop_back();
}
void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
{ {
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
@ -130,6 +147,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get()); auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get());
if (!lengthType || lengthType->isFractional()) if (!lengthType || lengthType->isFractional())
fatalTypeError(length->location(), "Invalid array length, expected integer literal."); fatalTypeError(length->location(), "Invalid array length, expected integer literal.");
else if (lengthType->isNegative())
fatalTypeError(length->location(), "Array with negative length specified.");
else else
_typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
} }
@ -159,7 +178,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
bool ReferencesResolver::visit(Return const& _return) bool ReferencesResolver::visit(Return const& _return)
{ {
_return.annotation().functionReturnParameters = m_returnParameters; solAssert(!m_returnParameters.empty(), "");
_return.annotation().functionReturnParameters = m_returnParameters.back();
return true; return true;
} }

View File

@ -45,22 +45,24 @@ public:
ReferencesResolver( ReferencesResolver(
ErrorList& _errors, ErrorList& _errors,
NameAndTypeResolver& _resolver, NameAndTypeResolver& _resolver,
ParameterList const* _returnParameters,
bool _resolveInsideCode = false bool _resolveInsideCode = false
): ):
m_errors(_errors), m_errors(_errors),
m_resolver(_resolver), m_resolver(_resolver),
m_returnParameters(_returnParameters),
m_resolveInsideCode(_resolveInsideCode) m_resolveInsideCode(_resolveInsideCode)
{} {}
/// @returns true if no errors during resolving /// @returns true if no errors during resolving and throws exceptions on fatal errors.
bool resolve(ASTNode const& _root); bool resolve(ASTNode const& _root);
private: private:
virtual bool visit(Block const&) override { return m_resolveInsideCode; } virtual bool visit(Block const&) override { return m_resolveInsideCode; }
virtual bool visit(Identifier const& _identifier) override; virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(ElementaryTypeName const& _typeName) override;
virtual bool visit(FunctionDefinition const& _functionDefinition) override;
virtual void endVisit(FunctionDefinition const& _functionDefinition) override;
virtual bool visit(ModifierDefinition const& _modifierDefinition) override;
virtual void endVisit(ModifierDefinition const& _modifierDefinition) override;
virtual void endVisit(UserDefinedTypeName const& _typeName) override; virtual void endVisit(UserDefinedTypeName const& _typeName) override;
virtual void endVisit(FunctionTypeName const& _typeName) override; virtual void endVisit(FunctionTypeName const& _typeName) override;
virtual void endVisit(Mapping const& _typeName) override; virtual void endVisit(Mapping const& _typeName) override;
@ -83,7 +85,8 @@ private:
ErrorList& m_errors; ErrorList& m_errors;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ParameterList const* m_returnParameters; /// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;
bool const m_resolveInsideCode; bool const m_resolveInsideCode;
bool m_errorOccurred = false; bool m_errorOccurred = false;
}; };

View File

@ -34,6 +34,9 @@ class SemVerError: dev::Exception
{ {
}; };
#undef major
#undef minor
struct SemVerVersion struct SemVerVersion
{ {
unsigned numbers[3]; unsigned numbers[3];

View File

@ -36,6 +36,9 @@ namespace solidity
/** /**
* The module that performs static analysis on the AST. * The module that performs static analysis on the AST.
* In this context, static analysis is anything that can produce warnings which can help
* programmers write cleaner code. For every warning generated eher, it has to be possible to write
* equivalent code that does generate the warning.
*/ */
class StaticAnalyzer: private ASTConstVisitor class StaticAnalyzer: private ASTConstVisitor
{ {

View File

@ -26,9 +26,9 @@ using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit) bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot)
{ {
_sourceUnit.accept(*this); _astRoot.accept(*this);
return Error::containsOnlyWarnings(m_errors); return Error::containsOnlyWarnings(m_errors);
} }

View File

@ -39,7 +39,7 @@ public:
/// @param _errors the reference to the list of errors and warnings to add them found during type checking. /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
SyntaxChecker(ErrorList& _errors): m_errors(_errors) {} SyntaxChecker(ErrorList& _errors): m_errors(_errors) {}
bool checkSyntax(SourceUnit const& _sourceUnit); bool checkSyntax(ASTNode const& _astRoot);
private: private:
/// Adds a new error to the list of errors. /// Adds a new error to the list of errors.

View File

@ -32,11 +32,11 @@ using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
bool TypeChecker::checkTypeRequirements(ContractDefinition const& _contract) bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
{ {
try try
{ {
visit(_contract); _contract.accept(*this);
} }
catch (FatalError const&) catch (FatalError const&)
{ {
@ -77,8 +77,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
FunctionDefinition const* function = _contract.constructor(); FunctionDefinition const* function = _contract.constructor();
if (function) if (function)
{ {
if (!function->isPublic())
_contract.annotation().hasPublicConstructor = false;
if (!function->returnParameters().empty()) if (!function->returnParameters().empty())
typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (function->isDeclaredConst()) if (function->isDeclaredConst())
@ -427,7 +425,9 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary(); bool isLibraryFunction =
dynamic_cast<ContractDefinition const*>(_function.scope()) &&
dynamic_cast<ContractDefinition const*>(_function.scope())->isLibrary();
if (_function.isPayable()) if (_function.isPayable())
{ {
if (isLibraryFunction) if (isLibraryFunction)
@ -467,27 +467,29 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
// TypeChecker at the VariableDeclarationStatement level. // TypeChecker at the VariableDeclarationStatement level.
TypePointer varType = _variable.annotation().type; TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Failed to infer variable 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()) if (_variable.value())
expectType(*_variable.value(), *varType); expectType(*_variable.value(), *varType);
if (_variable.isConstant())
{
if (!_variable.isStateVariable())
typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
if (!_variable.type()->isValueType())
{
bool allowed = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(_variable.type().get()))
allowed = arrayType->isString();
if (!allowed)
typeError(_variable.location(), "Constants of non-value type not yet implemented.");
}
if (!_variable.value())
typeError(_variable.location(), "Uninitialized \"constant\" variable.");
else if (!_variable.value()->annotation().isPure)
warning(
_variable.value()->location(),
"Initial value for constant variable has to be compile-time constant. "
"This will fail to compile with the next breaking version change."
);
}
if (!_variable.isStateVariable()) if (!_variable.isStateVariable())
{ {
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
@ -590,8 +592,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
// Inline assembly does not have its own type-checking phase, so we just run the // Inline assembly does not have its own type-checking phase, so we just run the
// code-generator and see whether it produces any errors. // code-generator and see whether it produces any errors.
// External references have already been resolved in a prior stage and stored in the annotation. // External references have already been resolved in a prior stage and stored in the annotation.
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors); auto identifierAccess = [&](
codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) { assembly::Identifier const& _identifier,
eth::Assembly& _assembly,
assembly::CodeGenerator::IdentifierContext _context
)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end()) if (ref == _inlineAssembly.annotation().externalReferences.end())
return false; return false;
@ -609,7 +615,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly."); fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
if (var->isLocalVariable()) if (var->isLocalVariable())
pushes = var->type()->sizeOnStack(); pushes = var->type()->sizeOnStack();
else if (var->type()->isValueType()) else if (!var->type()->isValueType())
pushes = 1; pushes = 1;
else else
pushes = 2; // slot number, intra slot offset pushes = 2; // slot number, intra slot offset
@ -639,8 +645,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return false; return false;
} }
return true; return true;
}); };
return false; assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
if (!codeGen.typeCheck(identifierAccess))
return false;
return true;
} }
bool TypeChecker::visit(IfStatement const& _ifStatement) bool TypeChecker::visit(IfStatement const& _ifStatement)
@ -728,13 +737,16 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get())) if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
{ {
if (ref->dataStoredIn(DataLocation::Storage)) if (ref->dataStoredIn(DataLocation::Storage))
{
warning( warning(
varDecl.location(), varDecl.location(),
"Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?" "Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"
); );
}
} }
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
typeError(
varDecl.location(),
"Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable."
);
varDecl.accept(*this); varDecl.accept(*this);
return false; return false;
} }
@ -822,6 +834,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
else else
solAssert(false, ""); solAssert(false, "");
} }
else if (*var.annotation().type == TupleType())
typeError(
var.location(),
"Cannot declare variable with void (empty tuple) type."
);
var.accept(*this); var.accept(*this);
} }
else else
@ -874,10 +891,11 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
if ( if (
location == Location::Bare || location == Location::Bare ||
location == Location::BareCallCode || location == Location::BareCallCode ||
location == Location::BareDelegateCall || location == Location::BareDelegateCall
location == Location::Send
) )
warning(_statement.location(), "Return value of low-level calls not used."); warning(_statement.location(), "Return value of low-level calls not used.");
else if (location == Location::Send)
warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead.");
} }
} }
} }
@ -913,6 +931,10 @@ bool TypeChecker::visit(Conditional const& _conditional)
} }
_conditional.annotation().type = commonType; _conditional.annotation().type = commonType;
_conditional.annotation().isPure =
_conditional.condition().annotation().isPure &&
_conditional.trueExpression().annotation().isPure &&
_conditional.falseExpression().annotation().isPure;
if (_conditional.annotation().lValueRequested) if (_conditional.annotation().lValueRequested)
typeError( typeError(
@ -930,6 +952,11 @@ bool TypeChecker::visit(Assignment const& _assignment)
_assignment.annotation().type = t; _assignment.annotation().type = t;
if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get())) if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get()))
{ {
if (_assignment.assignmentOperator() != Token::Assign)
typeError(
_assignment.location(),
"Compound assignment is not allowed for tuple types."
);
// Sequenced assignments of tuples is not valid, make the result a "void" type. // Sequenced assignments of tuples is not valid, make the result a "void" type.
_assignment.annotation().type = make_shared<TupleType>(); _assignment.annotation().type = make_shared<TupleType>();
expectType(_assignment.rightHandSide(), *tupleType); expectType(_assignment.rightHandSide(), *tupleType);
@ -989,6 +1016,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
} }
else else
{ {
bool isPure = true;
TypePointer inlineArrayType; TypePointer inlineArrayType;
for (size_t i = 0; i < components.size(); ++i) for (size_t i = 0; i < components.size(); ++i)
{ {
@ -1007,14 +1035,17 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
fatalTypeError(components[i]->location(), "Invalid mobile type."); fatalTypeError(components[i]->location(), "Invalid mobile type.");
if (i == 0) if (i == 0)
inlineArrayType = types[i]; inlineArrayType = types[i]->mobileType();
else if (inlineArrayType) else if (inlineArrayType)
inlineArrayType = Type::commonType(inlineArrayType, types[i]); inlineArrayType = Type::commonType(inlineArrayType, types[i]);
} }
if (!components[i]->annotation().isPure)
isPure = false;
} }
else else
types.push_back(TypePointer()); types.push_back(TypePointer());
} }
_tuple.annotation().isPure = isPure;
if (_tuple.isInlineArray()) if (_tuple.isInlineArray())
{ {
if (!inlineArrayType) if (!inlineArrayType)
@ -1041,7 +1072,8 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
{ {
// Inc, Dec, Add, Sub, Not, BitNot, Delete // Inc, Dec, Add, Sub, Not, BitNot, Delete
Token::Value op = _operation.getOperator(); Token::Value op = _operation.getOperator();
if (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete) bool const modifying = (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete);
if (modifying)
requireLValue(_operation.subExpression()); requireLValue(_operation.subExpression());
else else
_operation.subExpression().accept(*this); _operation.subExpression().accept(*this);
@ -1059,6 +1091,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
t = subExprType; t = subExprType;
} }
_operation.annotation().type = t; _operation.annotation().type = t;
_operation.annotation().isPure = !modifying && _operation.subExpression().annotation().isPure;
return false; return false;
} }
@ -1085,6 +1118,30 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
Token::isCompareOp(_operation.getOperator()) ? Token::isCompareOp(_operation.getOperator()) ?
make_shared<BoolType>() : make_shared<BoolType>() :
commonType; commonType;
_operation.annotation().isPure =
_operation.leftExpression().annotation().isPure &&
_operation.rightExpression().annotation().isPure;
if (_operation.getOperator() == Token::Exp)
{
if (
leftType->category() == Type::Category::RationalNumber &&
rightType->category() != Type::Category::RationalNumber
)
if ((
commonType->category() == Type::Category::Integer &&
dynamic_cast<IntegerType const&>(*commonType).numBits() != 256
) || (
commonType->category() == Type::Category::FixedPoint &&
dynamic_cast<FixedPointType const&>(*commonType).numBits() != 256
))
warning(
_operation.location(),
"Result of exponentiation has type " + commonType->toString() + " and thus "
"might overflow. Silence this warning by converting the literal to the "
"expected type."
);
}
} }
bool TypeChecker::visit(FunctionCall const& _functionCall) bool TypeChecker::visit(FunctionCall const& _functionCall)
@ -1093,6 +1150,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names();
bool isPure = true;
// We need to check arguments' type first as they will be needed for overload resolution. // We need to check arguments' type first as they will be needed for overload resolution.
shared_ptr<TypePointers> argumentTypes; shared_ptr<TypePointers> argumentTypes;
if (isPositionalCall) if (isPositionalCall)
@ -1100,6 +1159,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
for (ASTPointer<Expression const> const& argument: arguments) for (ASTPointer<Expression const> const& argument: arguments)
{ {
argument->accept(*this); argument->accept(*this);
if (!argument->annotation().isPure)
isPure = false;
// only store them for positional calls // only store them for positional calls
if (isPositionalCall) if (isPositionalCall)
argumentTypes->push_back(type(*argument)); argumentTypes->push_back(type(*argument));
@ -1137,6 +1198,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
typeError(_functionCall.location(), "Explicit type conversion not allowed."); typeError(_functionCall.location(), "Explicit type conversion not allowed.");
} }
_functionCall.annotation().type = resultType; _functionCall.annotation().type = resultType;
_functionCall.annotation().isPure = isPure;
return false; return false;
} }
@ -1153,9 +1215,16 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
functionType = structType.constructorType(); functionType = structType.constructorType();
membersRemovedForStructConstructor = structType.membersMissingInMemory(); membersRemovedForStructConstructor = structType.membersMissingInMemory();
_functionCall.annotation().isPure = isPure;
} }
else else
{
functionType = dynamic_pointer_cast<FunctionType const>(expressionType); functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
_functionCall.annotation().isPure =
isPure &&
_functionCall.expression().annotation().isPure &&
functionType->isPure();
}
if (!functionType) if (!functionType)
{ {
@ -1283,7 +1352,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
fatalTypeError(_newExpression.location(), "Identifier is not a contract."); fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (!contract->annotation().isFullyImplemented) if (!contract->annotation().isFullyImplemented)
typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
if (!contract->annotation().hasPublicConstructor) if (!contract->constructorIsPublic())
typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
solAssert(!!m_scope, ""); solAssert(!!m_scope, "");
@ -1320,6 +1389,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
strings(), strings(),
FunctionType::Location::ObjectCreation FunctionType::Location::ObjectCreation
); );
_newExpression.annotation().isPure = true;
} }
else else
fatalTypeError(_newExpression.location(), "Contract or array type expected."); fatalTypeError(_newExpression.location(), "Contract or array type expected.");
@ -1405,6 +1475,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.isLValue = annotation.referencedDeclaration->isLValue();
} }
// TODO some members might be pure, but for example `address(0x123).balance` is not pure
// although every subexpression is, so leaving this limited for now.
if (auto tt = dynamic_cast<TypeType const*>(exprType.get()))
if (tt->actualType()->category() == Type::Category::Enum)
annotation.isPure = true;
return false; return false;
} }
@ -1414,6 +1490,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
TypePointer baseType = type(_access.baseExpression()); TypePointer baseType = type(_access.baseExpression());
TypePointer resultType; TypePointer resultType;
bool isLValue = false; bool isLValue = false;
bool isPure = _access.baseExpression().annotation().isPure;
Expression const* index = _access.indexExpression(); Expression const* index = _access.indexExpression();
switch (baseType->category()) switch (baseType->category())
{ {
@ -1495,6 +1572,9 @@ bool TypeChecker::visit(IndexAccess const& _access)
} }
_access.annotation().type = move(resultType); _access.annotation().type = move(resultType);
_access.annotation().isLValue = isLValue; _access.annotation().isLValue = isLValue;
if (index && !index->annotation().isPure)
isPure = false;
_access.annotation().isPure = isPure;
return false; return false;
} }
@ -1549,18 +1629,22 @@ bool TypeChecker::visit(Identifier const& _identifier)
!!annotation.referencedDeclaration, !!annotation.referencedDeclaration,
"Referenced declaration is null after overload resolution." "Referenced declaration is null after overload resolution."
); );
auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration);
annotation.isConstant = variableDeclaration != nullptr && variableDeclaration->isConstant();
annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.isLValue = annotation.referencedDeclaration->isLValue();
annotation.type = annotation.referencedDeclaration->type(); annotation.type = annotation.referencedDeclaration->type();
if (!annotation.type) if (!annotation.type)
fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined."); fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration))
annotation.isPure = annotation.isConstant = variableDeclaration->isConstant();
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
if (auto functionType = dynamic_cast<FunctionType const*>(annotation.type.get()))
annotation.isPure = functionType->isPure();
return false; return false;
} }
void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
{ {
_expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeName())); _expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeName()));
_expr.annotation().isPure = true;
} }
void TypeChecker::endVisit(Literal const& _literal) void TypeChecker::endVisit(Literal const& _literal)
@ -1573,9 +1657,14 @@ void TypeChecker::endVisit(Literal const& _literal)
return; return;
} }
else else
warning(_literal.location(), "This looks like an address but has an invalid checksum."); warning(
_literal.location(),
"This looks like an address but has an invalid checksum. "
"If this is not used as an address, please prepend '00'."
);
} }
_literal.annotation().type = Type::forLiteral(_literal); _literal.annotation().type = Type::forLiteral(_literal);
_literal.annotation().isPure = true;
if (!_literal.annotation().type) if (!_literal.annotation().type)
fatalTypeError(_literal.location(), "Invalid literal value."); fatalTypeError(_literal.location(), "Invalid literal value.");
} }

View File

@ -47,7 +47,7 @@ public:
/// Performs type checking on the given contract and all of its sub-nodes. /// 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 /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool checkTypeRequirements(ContractDefinition const& _contract); bool checkTypeRequirements(ASTNode const& _contract);
/// @returns the type of an expression and asserts that it is present. /// @returns the type of an expression and asserts that it is present.
TypePointer const& type(Expression const& _expression) const; TypePointer const& type(Expression const& _expression) const;

View File

@ -83,7 +83,7 @@ SourceUnitAnnotation& SourceUnit::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new SourceUnitAnnotation(); m_annotation = new SourceUnitAnnotation();
return static_cast<SourceUnitAnnotation&>(*m_annotation); return dynamic_cast<SourceUnitAnnotation&>(*m_annotation);
} }
string Declaration::sourceUnitName() const string Declaration::sourceUnitName() const
@ -99,7 +99,7 @@ ImportAnnotation& ImportDirective::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new ImportAnnotation(); m_annotation = new ImportAnnotation();
return static_cast<ImportAnnotation&>(*m_annotation); return dynamic_cast<ImportAnnotation&>(*m_annotation);
} }
TypePointer ImportDirective::type() const TypePointer ImportDirective::type() const
@ -132,6 +132,12 @@ FunctionDefinition const* ContractDefinition::constructor() const
return nullptr; return nullptr;
} }
bool ContractDefinition::constructorIsPublic() const
{
FunctionDefinition const* f = constructor();
return !f || f->isPublic();
}
FunctionDefinition const* ContractDefinition::fallbackFunction() const FunctionDefinition const* ContractDefinition::fallbackFunction() const
{ {
for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
@ -255,14 +261,14 @@ ContractDefinitionAnnotation& ContractDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new ContractDefinitionAnnotation(); m_annotation = new ContractDefinitionAnnotation();
return static_cast<ContractDefinitionAnnotation&>(*m_annotation); return dynamic_cast<ContractDefinitionAnnotation&>(*m_annotation);
} }
TypeNameAnnotation& TypeName::annotation() const TypeNameAnnotation& TypeName::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new TypeNameAnnotation(); m_annotation = new TypeNameAnnotation();
return static_cast<TypeNameAnnotation&>(*m_annotation); return dynamic_cast<TypeNameAnnotation&>(*m_annotation);
} }
TypePointer StructDefinition::type() const TypePointer StructDefinition::type() const
@ -274,7 +280,7 @@ TypeDeclarationAnnotation& StructDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new TypeDeclarationAnnotation(); m_annotation = new TypeDeclarationAnnotation();
return static_cast<TypeDeclarationAnnotation&>(*m_annotation); return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
} }
TypePointer EnumValue::type() const TypePointer EnumValue::type() const
@ -293,7 +299,7 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new TypeDeclarationAnnotation(); m_annotation = new TypeDeclarationAnnotation();
return static_cast<TypeDeclarationAnnotation&>(*m_annotation); return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
} }
shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const
@ -349,7 +355,7 @@ FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new FunctionDefinitionAnnotation(); m_annotation = new FunctionDefinitionAnnotation();
return static_cast<FunctionDefinitionAnnotation&>(*m_annotation); return dynamic_cast<FunctionDefinitionAnnotation&>(*m_annotation);
} }
TypePointer ModifierDefinition::type() const TypePointer ModifierDefinition::type() const
@ -361,7 +367,7 @@ ModifierDefinitionAnnotation& ModifierDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new ModifierDefinitionAnnotation(); m_annotation = new ModifierDefinitionAnnotation();
return static_cast<ModifierDefinitionAnnotation&>(*m_annotation); return dynamic_cast<ModifierDefinitionAnnotation&>(*m_annotation);
} }
TypePointer EventDefinition::type() const TypePointer EventDefinition::type() const
@ -381,14 +387,14 @@ EventDefinitionAnnotation& EventDefinition::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new EventDefinitionAnnotation(); m_annotation = new EventDefinitionAnnotation();
return static_cast<EventDefinitionAnnotation&>(*m_annotation); return dynamic_cast<EventDefinitionAnnotation&>(*m_annotation);
} }
UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new UserDefinedTypeNameAnnotation(); m_annotation = new UserDefinedTypeNameAnnotation();
return static_cast<UserDefinedTypeNameAnnotation&>(*m_annotation); return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
} }
bool VariableDeclaration::isLValue() const bool VariableDeclaration::isLValue() const
@ -460,70 +466,70 @@ VariableDeclarationAnnotation& VariableDeclaration::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new VariableDeclarationAnnotation(); m_annotation = new VariableDeclarationAnnotation();
return static_cast<VariableDeclarationAnnotation&>(*m_annotation); return dynamic_cast<VariableDeclarationAnnotation&>(*m_annotation);
} }
StatementAnnotation& Statement::annotation() const StatementAnnotation& Statement::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new StatementAnnotation(); m_annotation = new StatementAnnotation();
return static_cast<StatementAnnotation&>(*m_annotation); return dynamic_cast<StatementAnnotation&>(*m_annotation);
} }
InlineAssemblyAnnotation& InlineAssembly::annotation() const InlineAssemblyAnnotation& InlineAssembly::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new InlineAssemblyAnnotation(); m_annotation = new InlineAssemblyAnnotation();
return static_cast<InlineAssemblyAnnotation&>(*m_annotation); return dynamic_cast<InlineAssemblyAnnotation&>(*m_annotation);
} }
ReturnAnnotation& Return::annotation() const ReturnAnnotation& Return::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new ReturnAnnotation(); m_annotation = new ReturnAnnotation();
return static_cast<ReturnAnnotation&>(*m_annotation); return dynamic_cast<ReturnAnnotation&>(*m_annotation);
} }
VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new VariableDeclarationStatementAnnotation(); m_annotation = new VariableDeclarationStatementAnnotation();
return static_cast<VariableDeclarationStatementAnnotation&>(*m_annotation); return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
} }
ExpressionAnnotation& Expression::annotation() const ExpressionAnnotation& Expression::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new ExpressionAnnotation(); m_annotation = new ExpressionAnnotation();
return static_cast<ExpressionAnnotation&>(*m_annotation); return dynamic_cast<ExpressionAnnotation&>(*m_annotation);
} }
MemberAccessAnnotation& MemberAccess::annotation() const MemberAccessAnnotation& MemberAccess::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new MemberAccessAnnotation(); m_annotation = new MemberAccessAnnotation();
return static_cast<MemberAccessAnnotation&>(*m_annotation); return dynamic_cast<MemberAccessAnnotation&>(*m_annotation);
} }
BinaryOperationAnnotation& BinaryOperation::annotation() const BinaryOperationAnnotation& BinaryOperation::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new BinaryOperationAnnotation(); m_annotation = new BinaryOperationAnnotation();
return static_cast<BinaryOperationAnnotation&>(*m_annotation); return dynamic_cast<BinaryOperationAnnotation&>(*m_annotation);
} }
FunctionCallAnnotation& FunctionCall::annotation() const FunctionCallAnnotation& FunctionCall::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new FunctionCallAnnotation(); m_annotation = new FunctionCallAnnotation();
return static_cast<FunctionCallAnnotation&>(*m_annotation); return dynamic_cast<FunctionCallAnnotation&>(*m_annotation);
} }
IdentifierAnnotation& Identifier::annotation() const IdentifierAnnotation& Identifier::annotation() const
{ {
if (!m_annotation) if (!m_annotation)
m_annotation = new IdentifierAnnotation(); m_annotation = new IdentifierAnnotation();
return static_cast<IdentifierAnnotation&>(*m_annotation); return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
} }
bool Literal::looksLikeAddress() const bool Literal::looksLikeAddress() const

View File

@ -356,6 +356,8 @@ public:
/// Returns the constructor or nullptr if no constructor was specified. /// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* constructor() const; FunctionDefinition const* constructor() const;
/// @returns true iff the constructor of this contract is public (or non-existing).
bool constructorIsPublic() const;
/// Returns the fallback function or nullptr if no fallback function was specified. /// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* fallbackFunction() const; FunctionDefinition const* fallbackFunction() const;

View File

@ -80,8 +80,6 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota
{ {
/// Whether all functions are implemented. /// Whether all functions are implemented.
bool isFullyImplemented = true; bool isFullyImplemented = true;
/// Whether a public constructor (even the default one) is available.
bool hasPublicConstructor = true;
/// List of all (direct and indirect) base contracts in order from derived to /// List of all (direct and indirect) base contracts in order from derived to
/// base, including the contract itself. /// base, including the contract itself.
std::vector<ContractDefinition const*> linearizedBaseContracts; std::vector<ContractDefinition const*> linearizedBaseContracts;
@ -158,6 +156,8 @@ struct ExpressionAnnotation: ASTAnnotation
TypePointer type; TypePointer type;
/// Whether the expression is a constant variable /// Whether the expression is a constant variable
bool isConstant = false; bool isConstant = false;
/// Whether the expression is pure, i.e. compile-time constant.
bool isPure = false;
/// Whether it is an LValue (i.e. something that can be assigned to). /// Whether it is an LValue (i.e. something that can be assigned to).
bool isLValue = false; bool isLValue = false;
/// Whether the expression is used in a context where the LValue is actually required. /// Whether the expression is used in a context where the LValue is actually required.

View File

@ -32,6 +32,7 @@
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/sliced.hpp> #include <boost/range/adaptor/sliced.hpp>
#include <boost/range/adaptor/transformed.hpp> #include <boost/range/adaptor/transformed.hpp>
@ -252,9 +253,9 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
{ {
if (!_a || !_b) if (!_a || !_b)
return TypePointer(); return TypePointer();
else if (_b->isImplicitlyConvertibleTo(*_a->mobileType())) else if (_a->mobileType() && _b->isImplicitlyConvertibleTo(*_a->mobileType()))
return _a->mobileType(); return _a->mobileType();
else if (_a->isImplicitlyConvertibleTo(*_b->mobileType())) else if (_b->mobileType() && _a->isImplicitlyConvertibleTo(*_b->mobileType()))
return _b->mobileType(); return _b->mobileType();
else else
return TypePointer(); return TypePointer();
@ -464,7 +465,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Location::Transfer)}
}; };
else else
return MemberList::MemberMap(); return MemberList::MemberMap();
@ -570,39 +572,99 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi
return commonType; return commonType;
} }
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal) tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
{ {
rational x; rational value;
try try
{ {
rational numerator; auto radixPoint = find(_value.begin(), _value.end(), '.');
rational denominator(1);
if (radixPoint != _value.end())
auto radixPoint = find(_literal.value().begin(), _literal.value().end(), '.');
if (radixPoint != _literal.value().end())
{ {
if ( if (
!all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || !all_of(radixPoint + 1, _value.end(), ::isdigit) ||
!all_of(_literal.value().begin(), radixPoint, ::isdigit) !all_of(_value.begin(), radixPoint, ::isdigit)
) )
return make_tuple(false, rational(0)); return make_tuple(false, rational(0));
//Only decimal notation allowed here, leading zeros would switch to octal.
// Only decimal notation allowed here, leading zeros would switch to octal.
auto fractionalBegin = find_if_not( auto fractionalBegin = find_if_not(
radixPoint + 1, radixPoint + 1,
_literal.value().end(), _value.end(),
[](char const& a) { return a == '0'; } [](char const& a) { return a == '0'; }
); );
denominator = bigint(string(fractionalBegin, _literal.value().end())); rational numerator;
rational denominator(1);
denominator = bigint(string(fractionalBegin, _value.end()));
denominator /= boost::multiprecision::pow( denominator /= boost::multiprecision::pow(
bigint(10), bigint(10),
distance(radixPoint + 1, _literal.value().end()) distance(radixPoint + 1, _value.end())
); );
numerator = bigint(string(_literal.value().begin(), radixPoint)); numerator = bigint(string(_value.begin(), radixPoint));
x = numerator + denominator; value = numerator + denominator;
} }
else else
x = bigint(_literal.value()); value = bigint(_value);
return make_tuple(true, value);
}
catch (...)
{
return make_tuple(false, rational(0));
}
}
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
try
{
auto expPoint = find(_literal.value().begin(), _literal.value().end(), 'e');
if (expPoint == _literal.value().end())
expPoint = find(_literal.value().begin(), _literal.value().end(), 'E');
if (boost::starts_with(_literal.value(), "0x"))
{
// process as hex
value = bigint(_literal.value());
}
else if (expPoint != _literal.value().end())
{
// parse the exponent
bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
return make_tuple(false, rational(0));
// parse the base
tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint));
if (!get<0>(base))
return make_tuple(false, rational(0));
value = get<1>(base);
if (exp < 0)
{
exp *= -1;
value /= boost::multiprecision::pow(
bigint(10),
exp.convert_to<int32_t>()
);
}
else
value *= boost::multiprecision::pow(
bigint(10),
exp.convert_to<int32_t>()
);
}
else
{
// parse as rational number
tuple<bool, rational> tmp = parseRational(_literal.value());
if (!get<0>(tmp))
return tmp;
value = get<1>(tmp);
}
} }
catch (...) catch (...)
{ {
@ -615,33 +677,33 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
case Literal::SubDenomination::Second: case Literal::SubDenomination::Second:
break; break;
case Literal::SubDenomination::Szabo: case Literal::SubDenomination::Szabo:
x *= bigint("1000000000000"); value *= bigint("1000000000000");
break; break;
case Literal::SubDenomination::Finney: case Literal::SubDenomination::Finney:
x *= bigint("1000000000000000"); value *= bigint("1000000000000000");
break; break;
case Literal::SubDenomination::Ether: case Literal::SubDenomination::Ether:
x *= bigint("1000000000000000000"); value *= bigint("1000000000000000000");
break; break;
case Literal::SubDenomination::Minute: case Literal::SubDenomination::Minute:
x *= bigint("60"); value *= bigint("60");
break; break;
case Literal::SubDenomination::Hour: case Literal::SubDenomination::Hour:
x *= bigint("3600"); value *= bigint("3600");
break; break;
case Literal::SubDenomination::Day: case Literal::SubDenomination::Day:
x *= bigint("86400"); value *= bigint("86400");
break; break;
case Literal::SubDenomination::Week: case Literal::SubDenomination::Week:
x *= bigint("604800"); value *= bigint("604800");
break; break;
case Literal::SubDenomination::Year: case Literal::SubDenomination::Year:
x *= bigint("31536000"); value *= bigint("31536000");
break; break;
} }
return make_tuple(true, x); return make_tuple(true, value);
} }
bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -649,12 +711,12 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (_convertTo.category() == Category::Integer) if (_convertTo.category() == Category::Integer)
{ {
auto targetType = dynamic_cast<IntegerType const*>(&_convertTo); auto targetType = dynamic_cast<IntegerType const*>(&_convertTo);
if (m_value == 0) if (m_value == rational(0))
return true; return true;
if (isFractional()) if (isFractional())
return false; return false;
int forSignBit = (targetType->isSigned() ? 1 : 0); int forSignBit = (targetType->isSigned() ? 1 : 0);
if (m_value > 0) if (m_value > rational(0))
{ {
if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) if (m_value.numerator() <= (u256(-1) >> (256 - targetType->numBits() + forSignBit)))
return true; return true;
@ -775,13 +837,13 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
value = m_value * other.m_value; value = m_value * other.m_value;
break; break;
case Token::Div: case Token::Div:
if (other.m_value == 0) if (other.m_value == rational(0))
return TypePointer(); return TypePointer();
else else
value = m_value / other.m_value; value = m_value / other.m_value;
break; break;
case Token::Mod: case Token::Mod:
if (other.m_value == 0) if (other.m_value == rational(0))
return TypePointer(); return TypePointer();
else if (fractional) else if (fractional)
{ {
@ -886,7 +948,7 @@ u256 RationalNumberType::literalValue(Literal const*) const
solAssert(shiftedValue <= u256(-1), "Integer constant too large."); solAssert(shiftedValue <= u256(-1), "Integer constant too large.");
solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small.");
if (m_value >= 0) if (m_value >= rational(0))
value = u256(shiftedValue); value = u256(shiftedValue);
else else
value = s2u(s256(shiftedValue)); value = s2u(s256(shiftedValue));
@ -1652,6 +1714,7 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
{ {
TypePointer type = variable->annotation().type; TypePointer type = variable->annotation().type;
solAssert(type, "");
// Skip all mapping members if we are not in storage. // Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue; continue;
@ -1895,7 +1958,10 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
size_t si = fillRight ? i : components().size() - i - 1; size_t si = fillRight ? i : components().size() - i - 1;
size_t ti = fillRight ? i : targetComponents.size() - i - 1; size_t ti = fillRight ? i : targetComponents.size() - i - 1;
if (components()[si] && targetComponents[ti]) if (components()[si] && targetComponents[ti])
{
tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]); tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]);
solAssert(tempComponents[ti], "");
}
} }
return make_shared<TupleType>(tempComponents); return make_shared<TupleType>(tempComponents);
} }
@ -1964,6 +2030,8 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto structType = dynamic_cast<StructType const*>(returnType.get())) if (auto structType = dynamic_cast<StructType const*>(returnType.get()))
{ {
for (auto const& member: structType->members(nullptr)) for (auto const& member: structType->members(nullptr))
{
solAssert(member.type, "");
if (member.type->category() != Category::Mapping) if (member.type->category() != Category::Mapping)
{ {
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get())) if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get()))
@ -1972,6 +2040,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
retParams.push_back(member.type); retParams.push_back(member.type);
retParamNames.push_back(member.name); retParamNames.push_back(member.name);
} }
}
} }
else else
{ {
@ -2093,8 +2162,10 @@ string FunctionType::identifier() const
case Location::BareDelegateCall: id += "baredelegatecall"; break; case Location::BareDelegateCall: id += "baredelegatecall"; break;
case Location::Creation: id += "creation"; break; case Location::Creation: id += "creation"; break;
case Location::Send: id += "send"; break; case Location::Send: id += "send"; break;
case Location::Transfer: id += "transfer"; break;
case Location::SHA3: id += "sha3"; break; case Location::SHA3: id += "sha3"; break;
case Location::Selfdestruct: id += "selfdestruct"; break; case Location::Selfdestruct: id += "selfdestruct"; break;
case Location::Revert: id += "revert"; break;
case Location::ECRecover: id += "ecrecover"; break; case Location::ECRecover: id += "ecrecover"; break;
case Location::SHA256: id += "sha256"; break; case Location::SHA256: id += "sha256"; break;
case Location::RIPEMD160: id += "ripemd160"; break; case Location::RIPEMD160: id += "ripemd160"; break;
@ -2158,6 +2229,17 @@ bool FunctionType::operator==(Type const& _other) const
return true; return true;
} }
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_location == Location::External && _convertTo.category() == Category::Integer)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isAddress())
return true;
}
return _convertTo.category() == category();
}
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
{ {
if (_operator == Token::Value::Delete) if (_operator == Token::Value::Delete)
@ -2435,6 +2517,18 @@ u256 FunctionType::externalIdentifier() const
return FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(externalSignature()))); return FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(externalSignature())));
} }
bool FunctionType::isPure() const
{
return
m_location == Location::SHA3 ||
m_location == Location::ECRecover ||
m_location == Location::SHA256 ||
m_location == Location::RIPEMD160 ||
m_location == Location::AddMod ||
m_location == Location::MulMod ||
m_location == Location::ObjectCreation;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
{ {
TypePointers pointers; TypePointers pointers;
@ -2472,7 +2566,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
{ {
auto refType = dynamic_cast<ReferenceType const*>(t.get()); auto refType = dynamic_cast<ReferenceType const*>(t.get());
if (refType && refType->location() == DataLocation::CallData) if (refType && refType->location() == DataLocation::CallData)
parameterTypes.push_back(refType->copyForLocation(DataLocation::Memory, false)); parameterTypes.push_back(refType->copyForLocation(DataLocation::Memory, true));
else else
parameterTypes.push_back(t); parameterTypes.push_back(t);
} }

View File

@ -411,8 +411,14 @@ public:
/// @returns true if the value is not an integer. /// @returns true if the value is not an integer.
bool isFractional() const { return m_value.denominator() != 1; } bool isFractional() const { return m_value.denominator() != 1; }
/// @returns true if the value is negative.
bool isNegative() const { return m_value < 0; }
private: private:
rational m_value; rational m_value;
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
}; };
/** /**
@ -816,15 +822,17 @@ public:
{ {
Internal, ///< stack-call using plain JUMP Internal, ///< stack-call using plain JUMP
External, ///< external call using CALL External, ///< external call using CALL
CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage
Bare, ///< CALL without function hash Bare, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash BareDelegateCall, ///< DELEGATECALL without function hash
Creation, ///< external call using CREATE Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
SHA3, ///< SHA3 SHA3, ///< SHA3
Selfdestruct, ///< SELFDESTRUCT Selfdestruct, ///< SELFDESTRUCT
Revert, ///< REVERT
ECRecover, ///< CALL to special contract for ecrecover ECRecover, ///< CALL to special contract for ecrecover
SHA256, ///< CALL to special contract for sha256 SHA256, ///< CALL to special contract for sha256
RIPEMD160, ///< CALL to special contract for ripemd160 RIPEMD160, ///< CALL to special contract for ripemd160
@ -841,7 +849,9 @@ public:
MulMod, ///< MULMOD MulMod, ///< MULMOD
ArrayPush, ///< .push() to a dynamically sized array in storage ArrayPush, ///< .push() to a dynamically sized array in storage
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
ObjectCreation ///< array creation using new ObjectCreation, ///< array creation using new
Assert, ///< assert()
Require ///< require()
}; };
virtual Category category() const override { return Category::Function; } virtual Category category() const override { return Category::Function; }
@ -922,6 +932,7 @@ public:
virtual std::string identifier() const override; virtual std::string identifier() const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual std::string canonicalName(bool /*_addDataLocation*/) const override; virtual std::string canonicalName(bool /*_addDataLocation*/) const override;
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
@ -965,6 +976,10 @@ public:
} }
bool hasDeclaration() const { return !!m_declaration; } bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_isConstant; }
/// @returns true if the the result of this function only depends on its arguments
/// and it does not modify the state.
/// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const;
bool isPayable() const { return m_isPayable; } bool isPayable() const { return m_isPayable; }
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation

View File

@ -200,6 +200,7 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer // leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++; dynPointers++;
solAssert((argSize + dynPointers) < 16, "Stack too deep, try using less variables.");
} }
else else
{ {
@ -787,6 +788,20 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
if (_cleanupNeeded) if (_cleanupNeeded)
m_context << Instruction::ISZERO << Instruction::ISZERO; m_context << Instruction::ISZERO << Instruction::ISZERO;
break; break;
case Type::Category::Function:
{
if (targetTypeCategory == Type::Category::Integer)
{
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
solAssert(targetType.isAddress(), "Function type can only be converted to address.");
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
solAssert(typeOnStack.location() == FunctionType::Location::External, "Only external function type can be converted.");
// stack: <address> <function_id>
m_context << Instruction::POP;
break;
}
}
default: default:
// All other types should not be convertible to non-equal types. // All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");

View File

@ -42,7 +42,7 @@ class StackHeightChecker
public: public:
StackHeightChecker(CompilerContext const& _context): StackHeightChecker(CompilerContext const& _context):
m_context(_context), stackHeight(m_context.stackHeight()) {} m_context(_context), stackHeight(m_context.stackHeight()) {}
void check() { solAssert(m_context.stackHeight() == stackHeight, "I sense a disturbance in the stack."); } void check() { solAssert(m_context.stackHeight() == stackHeight, std::string("I sense a disturbance in the stack: ") + std::to_string(m_context.stackHeight()) + " vs " + std::to_string(stackHeight)); }
private: private:
CompilerContext const& m_context; CompilerContext const& m_context;
unsigned stackHeight; unsigned stackHeight;
@ -762,7 +762,9 @@ bool ContractCompiler::visit(Return const& _return)
bool ContractCompiler::visit(Throw const& _throw) bool ContractCompiler::visit(Throw const& _throw)
{ {
CompilerContext::LocationSetter locationSetter(m_context, _throw); CompilerContext::LocationSetter locationSetter(m_context, _throw);
m_context.appendJumpTo(m_context.errorTag()); // Do not send back an error detail.
m_context << u256(0) << u256(0);
m_context << Instruction::REVERT;
return false; return false;
} }

View File

@ -220,6 +220,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType( rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
_assignment.leftHandSide().annotation().type _assignment.leftHandSide().annotation().type
); );
solAssert(rightIntermediateType, "");
utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded); utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded);
_assignment.leftHandSide().accept(*this); _assignment.leftHandSide().accept(*this);
@ -395,6 +396,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
TypePointer leftTargetType = commonType; TypePointer leftTargetType = commonType;
TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType;
solAssert(rightTargetType, "");
// for commutative operators, push the literal as late as possible to allow improved optimization // for commutative operators, push the literal as late as possible to allow improved optimization
auto isLiteral = [](Expression const& _e) auto isLiteral = [](Expression const& _e)
@ -616,6 +618,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments.front()->accept(*this); arguments.front()->accept(*this);
break; break;
case Location::Send: case Location::Send:
case Location::Transfer:
_functionCall.expression().accept(*this); _functionCall.expression().accept(*this);
// Provide the gas stipend manually at first because we may send zero ether. // Provide the gas stipend manually at first because we may send zero ether.
// Will be zeroed if we send more than zero ether. // Will be zeroed if we send more than zero ether.
@ -644,11 +647,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
), ),
{} {}
); );
if (function.location() == Location::Transfer)
{
// Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO;
m_context.appendConditionalInvalid();
}
break; break;
case Location::Selfdestruct: case Location::Selfdestruct:
arguments.front()->accept(*this); arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
m_context << Instruction::SUICIDE; m_context << Instruction::SELFDESTRUCT;
break;
case Location::Revert:
// memory offset returned - zero length
m_context << u256(0) << u256(0);
m_context << Instruction::REVERT;
break; break;
case Location::SHA3: case Location::SHA3:
{ {
@ -803,6 +817,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[0]->accept(*this); arguments[0]->accept(*this);
// stack: newLength storageSlot slotOffset argValue // stack: newLength storageSlot slotOffset argValue
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
solAssert(type, "");
utils().convertType(*arguments[0]->annotation().type, *type); utils().convertType(*arguments[0]->annotation().type, *type);
utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack());
utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack());
@ -863,6 +878,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::POP; m_context << Instruction::POP;
break; break;
} }
case Location::Assert:
case Location::Require:
{
arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false);
// jump if condition was met
m_context << Instruction::ISZERO << Instruction::ISZERO;
auto success = m_context.appendConditionalJump();
if (function.location() == Location::Assert)
// condition was not met, flag an error
m_context << Instruction::INVALID;
else
m_context << u256(0) << u256(0) << Instruction::REVERT;
// the success branch
m_context << success;
break;
}
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
} }
@ -941,6 +973,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
case FunctionType::Location::Bare: case FunctionType::Location::Bare:
case FunctionType::Location::BareCallCode: case FunctionType::Location::BareCallCode:
case FunctionType::Location::BareDelegateCall: case FunctionType::Location::BareDelegateCall:
case FunctionType::Location::Transfer:
_memberAccess.expression().accept(*this); _memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier(); m_context << funType->externalIdentifier();
break; break;
@ -1022,7 +1055,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
); );
m_context << Instruction::BALANCE; m_context << Instruction::BALANCE;
} }
else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member)) else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
utils().convertType( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
IntegerType(0, IntegerType::Modifier::Address), IntegerType(0, IntegerType::Modifier::Address),

View File

@ -0,0 +1,180 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Analyzer part of inline assembly.
*/
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/Utils.h>
#include <boost/range/adaptor/reversed.hpp>
#include <memory>
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
bool Scope::registerLabel(string const& _name)
{
if (exists(_name))
return false;
identifiers[_name] = Label();
return true;
}
bool Scope::registerVariable(string const& _name)
{
if (exists(_name))
return false;
identifiers[_name] = Variable();
return true;
}
bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns)
{
if (exists(_name))
return false;
identifiers[_name] = Function(_arguments, _returns);
return true;
}
Scope::Identifier* Scope::lookup(string const& _name)
{
if (identifiers.count(_name))
return &identifiers[_name];
else if (superScope && !closedScope)
return superScope->lookup(_name);
else
return nullptr;
}
bool Scope::exists(string const& _name)
{
if (identifiers.count(_name))
return true;
else if (superScope)
return superScope->exists(_name);
else
return false;
}
AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors):
m_scopes(_scopes), m_errors(_errors)
{
// Make the Solidity ErrorTag available to inline assembly
m_scopes[nullptr] = make_shared<Scope>();
Scope::Label errorLabel;
errorLabel.id = Scope::Label::errorLabelId;
m_scopes[nullptr]->identifiers["invalidJumpLabel"] = errorLabel;
m_currentScope = m_scopes[nullptr].get();
}
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
{
if (!_literal.isNumber && _literal.value.size() > 32)
{
m_errors.push_back(make_shared<Error>(
Error::Type::TypeError,
"String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)"
));
return false;
}
return true;
}
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
if (!boost::apply_visitor(*this, arg))
success = false;
if (!(*this)(_instr.instruction))
success = false;
return success;
}
bool AsmAnalyzer::operator()(Label const& _item)
{
if (!m_currentScope->registerLabel(_item.name))
{
//@TODO secondary location
m_errors.push_back(make_shared<Error>(
Error::Type::DeclarationError,
"Label name " + _item.name + " already taken in this scope.",
_item.location
));
return false;
}
return true;
}
bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
{
return boost::apply_visitor(*this, *_assignment.value);
}
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{
bool success = boost::apply_visitor(*this, *_varDecl.value);
if (!m_currentScope->registerVariable(_varDecl.name))
{
//@TODO secondary location
m_errors.push_back(make_shared<Error>(
Error::Type::DeclarationError,
"Variable name " + _varDecl.name + " already taken in this scope.",
_varDecl.location
));
success = false;
}
return success;
}
bool AsmAnalyzer::operator()(assembly::FunctionDefinition const&)
{
// TODO - we cannot throw an exception here because of some tests.
return true;
}
bool AsmAnalyzer::operator()(assembly::FunctionCall const&)
{
// TODO - we cannot throw an exception here because of some tests.
return true;
}
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
auto scope = make_shared<Scope>();
scope->superScope = m_currentScope;
m_scopes[&_block] = scope;
m_currentScope = scope.get();
for (auto const& s: _block.statements)
if (!boost::apply_visitor(*this, s))
success = false;
m_currentScope = m_currentScope->superScope;
return success;
}

View File

@ -0,0 +1,158 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Analysis part of inline assembly.
*/
#pragma once
#include <libsolidity/interface/Exceptions.h>
#include <boost/variant.hpp>
#include <functional>
#include <memory>
namespace dev
{
namespace solidity
{
namespace assembly
{
struct Literal;
struct Block;
struct Label;
struct FunctionalInstruction;
struct FunctionalAssignment;
struct VariableDeclaration;
struct Instruction;
struct Identifier;
struct Assignment;
struct FunctionDefinition;
struct FunctionCall;
template <class...>
struct GenericVisitor{};
template <class Visitable, class... Others>
struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
{
using GenericVisitor<Others...>::operator ();
explicit GenericVisitor(
std::function<void(Visitable&)> _visitor,
std::function<void(Others&)>... _otherVisitors
):
GenericVisitor<Others...>(_otherVisitors...),
m_visitor(_visitor)
{}
void operator()(Visitable& _v) const { m_visitor(_v); }
std::function<void(Visitable&)> m_visitor;
};
template <>
struct GenericVisitor<>: public boost::static_visitor<> {
void operator()() const {}
};
struct Scope
{
struct Variable
{
int stackHeight = 0;
bool active = false;
};
struct Label
{
size_t id = unassignedLabelId;
static const size_t errorLabelId = -1;
static const size_t unassignedLabelId = 0;
};
struct Function
{
Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
size_t arguments = 0;
size_t returns = 0;
};
using Identifier = boost::variant<Variable, Label, Function>;
using Visitor = GenericVisitor<Variable const, Label const, Function const>;
using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
bool registerVariable(std::string const& _name);
bool registerLabel(std::string const& _name);
bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
/// Looks up the identifier in this or super scopes (stops and function and assembly boundaries)
/// and returns a valid pointer if found or a nullptr if not found.
/// The pointer will be invalidated if the scope is modified.
Identifier* lookup(std::string const& _name);
/// Looks up the identifier in this and super scopes (stops and function and assembly boundaries)
/// and calls the visitor, returns false if not found.
template <class V>
bool lookup(std::string const& _name, V const& _visitor)
{
if (Identifier* id = lookup(_name))
{
boost::apply_visitor(_visitor, *id);
return true;
}
else
return false;
}
/// @returns true if the name exists in this scope or in super scopes (also searches
/// across function and assembly boundaries).
bool exists(std::string const& _name);
Scope* superScope = nullptr;
/// If true, identifiers from the super scope are not visible here, but they are still
/// taken into account to prevent shadowing.
bool closedScope = false;
std::map<std::string, Identifier> identifiers;
};
class AsmAnalyzer: public boost::static_visitor<bool>
{
public:
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors);
bool operator()(assembly::Instruction const&) { return true; }
bool operator()(assembly::Literal const& _literal);
bool operator()(assembly::Identifier const&) { return true; }
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label);
bool operator()(assembly::Assignment const&) { return true; }
bool operator()(assembly::FunctionalAssignment const& _functionalAssignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
bool operator()(assembly::FunctionCall const& _functionCall);
bool operator()(assembly::Block const& _block);
private:
Scope* m_currentScope = nullptr;
Scopes& m_scopes;
ErrorList& m_errors;
};
}
}
}

View File

@ -21,14 +21,23 @@
*/ */
#include <libsolidity/inlineasm/AsmCodeGen.h> #include <libsolidity/inlineasm/AsmCodeGen.h>
#include <memory>
#include <functional> #include <libsolidity/inlineasm/AsmParser.h>
#include <libdevcore/CommonIO.h> #include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h> #include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmData.h> #include <libdevcore/CommonIO.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <memory>
#include <functional>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -42,73 +51,26 @@ struct GeneratorState
void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation()) void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation())
{ {
auto err = make_shared<Error>(_type); errors.push_back(make_shared<Error>(_type, _description, _location));
if (!_location.isEmpty())
*err << errinfo_sourceLocation(_location);
*err << errinfo_comment(_description);
errors.push_back(err);
} }
int const* findVariable(string const& _variableName) const size_t newLabelId()
{ {
auto localVariable = find_if( return assemblyTagToIdentifier(assembly.newTag());
variables.rbegin(),
variables.rend(),
[&](pair<string, int> const& _var) { return _var.first == _variableName; }
);
return localVariable != variables.rend() ? &localVariable->second : nullptr;
}
eth::AssemblyItem const* findLabel(string const& _labelName) const
{
auto label = find_if(
labels.begin(),
labels.end(),
[&](pair<string, eth::AssemblyItem> const& _label) { return _label.first == _labelName; }
);
return label != labels.end() ? &label->second : nullptr;
} }
map<string, eth::AssemblyItem> labels; size_t assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
vector<pair<string, int>> variables; ///< name plus stack height {
u256 id = _tag.data();
solAssert(id <= std::numeric_limits<size_t>::max(), "Tag id too large.");
return size_t(id);
}
std::map<assembly::Block const*, shared_ptr<Scope>> scopes;
ErrorList& errors; ErrorList& errors;
eth::Assembly& assembly; eth::Assembly& assembly;
}; };
/**
* Scans the inline assembly data for labels, creates tags in the assembly and searches for
* duplicate labels.
*/
class LabelOrganizer: public boost::static_visitor<>
{
public:
LabelOrganizer(GeneratorState& _state): m_state(_state)
{
// Make the Solidity ErrorTag available to inline assembly
m_state.labels.insert(make_pair("invalidJumpLabel", m_state.assembly.errorTag()));
}
template <class T>
void operator()(T const& /*_item*/) { }
void operator()(Label const& _item)
{
if (m_state.labels.count(_item.name))
//@TODO secondary location
m_state.addError(
Error::Type::DeclarationError,
"Label " + _item.name + " declared twice.",
_item.location
);
m_state.labels.insert(make_pair(_item.name, m_state.assembly.newTag()));
}
void operator()(assembly::Block const& _block)
{
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
}
private:
GeneratorState& m_state;
};
class CodeTransform: public boost::static_visitor<> class CodeTransform: public boost::static_visitor<>
{ {
public: public:
@ -117,14 +79,42 @@ public:
/// @param _identifierAccess used to resolve identifiers external to the inline assembly /// @param _identifierAccess used to resolve identifiers external to the inline assembly
explicit CodeTransform( explicit CodeTransform(
GeneratorState& _state, GeneratorState& _state,
assembly::Block const& _block,
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess() assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
): ):
m_state(_state) m_state(_state),
m_scope(*m_state.scopes.at(&_block)),
m_initialDeposit(m_state.assembly.deposit()),
m_identifierAccess(_identifierAccess)
{ {
if (_identifierAccess) std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
m_identifierAccess = _identifierAccess;
else m_state.assembly.setSourceLocation(_block.location);
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
// pop variables
for (auto const& identifier: m_scope.identifiers)
if (identifier.second.type() == typeid(Scope::Variable))
m_state.assembly.append(solidity::Instruction::POP);
int deposit = m_state.assembly.deposit() - m_initialDeposit;
// issue warnings for stack height discrepancies
if (deposit < 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
_block.location
);
}
else if (deposit > 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
_block.location
);
}
} }
void operator()(assembly::Instruction const& _instruction) void operator()(assembly::Instruction const& _instruction)
@ -137,40 +127,38 @@ public:
m_state.assembly.setSourceLocation(_literal.location); m_state.assembly.setSourceLocation(_literal.location);
if (_literal.isNumber) if (_literal.isNumber)
m_state.assembly.append(u256(_literal.value)); m_state.assembly.append(u256(_literal.value));
else if (_literal.value.size() > 32)
{
m_state.addError(
Error::Type::TypeError,
"String literal too long (" + boost::lexical_cast<string>(_literal.value.size()) + " > 32)"
);
m_state.assembly.append(u256(0));
}
else else
{
solAssert(_literal.value.size() <= 32, "");
m_state.assembly.append(_literal.value); m_state.assembly.append(_literal.value);
}
} }
void operator()(assembly::Identifier const& _identifier) void operator()(assembly::Identifier const& _identifier)
{ {
m_state.assembly.setSourceLocation(_identifier.location); m_state.assembly.setSourceLocation(_identifier.location);
// First search local variables, then labels, then externals. // First search internals, then externals.
if (int const* stackHeight = m_state.findVariable(_identifier.name)) if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor(
{ [=](Scope::Variable& _var)
int heightDiff = m_state.assembly.deposit() - *stackHeight;
if (heightDiff <= 0 || heightDiff > 16)
{ {
m_state.addError( if (int heightDiff = variableHeightDiff(_var, _identifier.location, false))
Error::Type::TypeError, m_state.assembly.append(solidity::dupInstruction(heightDiff));
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", else
_identifier.location // Store something to balance the stack
); m_state.assembly.append(u256(0));
m_state.assembly.append(u256(0)); },
[=](Scope::Label& _label)
{
assignLabelIdIfUnset(_label);
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
},
[=](Scope::Function&)
{
solAssert(false, "Not yet implemented");
} }
else )))
m_state.assembly.append(solidity::dupInstruction(heightDiff)); {
return;
} }
else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name)) else if (!m_identifierAccess || !m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
m_state.assembly.append(label->pushTag());
else if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
{ {
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
@ -190,10 +178,17 @@ public:
} }
(*this)(_instr.instruction); (*this)(_instr.instruction);
} }
void operator()(assembly::FunctionCall const&)
{
solAssert(false, "Function call not removed during desugaring phase.");
}
void operator()(Label const& _label) void operator()(Label const& _label)
{ {
m_state.assembly.setSourceLocation(_label.location); m_state.assembly.setSourceLocation(_label.location);
m_state.assembly.append(m_state.labels.at(_label.name)); solAssert(m_scope.identifiers.count(_label.name), "");
Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers[_label.name]);
assignLabelIdIfUnset(label);
m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id));
} }
void operator()(assembly::Assignment const& _assignment) void operator()(assembly::Assignment const& _assignment)
{ {
@ -213,69 +208,78 @@ public:
int height = m_state.assembly.deposit(); int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_varDecl.value); boost::apply_visitor(*this, *_varDecl.value);
expectDeposit(1, height, locationOf(*_varDecl.value)); expectDeposit(1, height, locationOf(*_varDecl.value));
m_state.variables.push_back(make_pair(_varDecl.name, height)); solAssert(m_scope.identifiers.count(_varDecl.name), "");
auto& var = boost::get<Scope::Variable>(m_scope.identifiers[_varDecl.name]);
var.stackHeight = height;
var.active = true;
} }
void operator()(assembly::Block const& _block) void operator()(assembly::Block const& _block)
{ {
size_t numVariables = m_state.variables.size(); CodeTransform(m_state, _block, m_identifierAccess);
int deposit = m_state.assembly.deposit(); }
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); void operator()(assembly::FunctionDefinition const&)
{
// pop variables solAssert(false, "Function definition not removed during desugaring phase.");
while (m_state.variables.size() > numVariables)
{
m_state.assembly.append(solidity::Instruction::POP);
m_state.variables.pop_back();
}
m_state.assembly.setSourceLocation(_block.location);
deposit = m_state.assembly.deposit() - deposit;
// issue warnings for stack height discrepancies
if (deposit < 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
_block.location
);
}
else if (deposit > 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
_block.location
);
}
} }
private: private:
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location) void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
{ {
if (int const* stackHeight = m_state.findVariable(_variableName.name)) if (m_scope.lookup(_variableName.name, Scope::Visitor(
{ [=](Scope::Variable const& _var)
int heightDiff = m_state.assembly.deposit() - *stackHeight - 1; {
if (heightDiff <= 0 || heightDiff > 16) if (int heightDiff = variableHeightDiff(_var, _location, true))
m_state.assembly.append(solidity::swapInstruction(heightDiff - 1));
m_state.assembly.append(solidity::Instruction::POP);
},
[=](Scope::Label const&)
{
m_state.addError( m_state.addError(
Error::Type::TypeError, Error::Type::DeclarationError,
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", "Label \"" + string(_variableName.name) + "\" used as variable."
_location
); );
else },
m_state.assembly.append(solidity::swapInstruction(heightDiff)); [=](Scope::Function const&)
m_state.assembly.append(solidity::Instruction::POP); {
return; m_state.addError(
Error::Type::DeclarationError,
"Function \"" + string(_variableName.name) + "\" used as variable."
);
}
)))
{
} }
else if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue)) else if (!m_identifierAccess || !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue." "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
); );
} }
/// Determines the stack height difference to the given variables. Automatically generates
/// errors if it is not yet in scope or the height difference is too large. Returns 0 on
/// errors and the (positive) stack height difference otherwise.
int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap)
{
if (!_var.active)
{
m_state.addError( Error::Type::TypeError, "Variable used before it was declared", _location);
return 0;
}
int heightDiff = m_state.assembly.deposit() - _var.stackHeight;
if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16))
{
m_state.addError(
Error::Type::TypeError,
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")",
_location
);
return 0;
}
else
return heightDiff;
}
void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location) void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location)
{ {
if (m_state.assembly.deposit() != _oldHeight + 1) if (m_state.assembly.deposit() != _oldHeight + 1)
@ -289,7 +293,19 @@ private:
); );
} }
/// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set.
void assignLabelIdIfUnset(Scope::Label& _label)
{
if (_label.id == Scope::Label::unassignedLabelId)
_label.id = m_state.newLabelId();
else if (_label.id == Scope::Label::errorLabelId)
_label.id = size_t(m_state.assembly.errorTag().data());
}
GeneratorState& m_state; GeneratorState& m_state;
Scope& m_scope;
int const m_initialDeposit;
assembly::CodeGenerator::IdentifierAccess m_identifierAccess; assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
}; };
@ -298,8 +314,9 @@ bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAcces
size_t initialErrorLen = m_errors.size(); size_t initialErrorLen = m_errors.size();
eth::Assembly assembly; eth::Assembly assembly;
GeneratorState state(m_errors, assembly); GeneratorState state(m_errors, assembly);
(LabelOrganizer(state))(m_parsedData); if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
(CodeTransform(state, _identifierAccess))(m_parsedData); return false;
CodeTransform(state, m_parsedData, _identifierAccess);
return m_errors.size() == initialErrorLen; return m_errors.size() == initialErrorLen;
} }
@ -307,15 +324,16 @@ eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::Identif
{ {
eth::Assembly assembly; eth::Assembly assembly;
GeneratorState state(m_errors, assembly); GeneratorState state(m_errors, assembly);
(LabelOrganizer(state))(m_parsedData); if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
(CodeTransform(state, _identifierAccess))(m_parsedData); solAssert(false, "Assembly error");
CodeTransform(state, m_parsedData, _identifierAccess);
return assembly; return assembly;
} }
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
{ {
GeneratorState state(m_errors, _assembly); GeneratorState state(m_errors, _assembly);
(LabelOrganizer(state))(m_parsedData); if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
(CodeTransform(state, _identifierAccess))(m_parsedData); solAssert(false, "Assembly error");
CodeTransform(state, m_parsedData, _identifierAccess);
} }

View File

@ -48,17 +48,22 @@ struct Label { SourceLocation location; std::string name; };
struct Assignment { SourceLocation location; Identifier variableName; }; struct Assignment { SourceLocation location; Identifier variableName; };
struct FunctionalAssignment; struct FunctionalAssignment;
struct VariableDeclaration; struct VariableDeclaration;
struct FunctionDefinition;
struct FunctionCall;
struct Block; struct Block;
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>; using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand /// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
/// side and requires x to occupy exactly one stack slot. /// side and requires x to occupy exactly one stack slot.
struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; }; struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; };
/// Functional instruction, e.g. "mul(mload(20), add(2, x))" /// Functional instruction, e.g. "mul(mload(20), add(2, x))"
struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; }; struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted /// Block-scope variable declaration ("let x := mload(20)"), non-hoisted
struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; }; struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; };
/// Block that creates a scope (frees declared stack variables) /// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; }; struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation> struct LocationExtractor: boost::static_visitor<SourceLocation>
{ {

View File

@ -62,6 +62,8 @@ assembly::Statement Parser::parseStatement()
{ {
case Token::Let: case Token::Let:
return parseVariableDeclaration(); return parseVariableDeclaration();
case Token::Function:
return parseFunctionDefinition();
case Token::LBrace: case Token::LBrace:
return parseBlock(); return parseBlock();
case Token::Assign: case Token::Assign:
@ -152,8 +154,8 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions()
s_instructions[name] = instruction.second; s_instructions[name] = instruction.second;
} }
// add alias for selfdestruct // add alias for suicide
s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT;
} }
return s_instructions; return s_instructions;
} }
@ -214,10 +216,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{ {
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let); expectToken(Token::Let);
varDecl.name = m_scanner->currentLiteral(); varDecl.name = expectAsmIdentifier();
if (instructions().count(varDecl.name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
expectToken(Token::Colon); expectToken(Token::Colon);
expectToken(Token::Assign); expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression())); varDecl.value.reset(new Statement(parseExpression()));
@ -225,44 +224,107 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
return varDecl; return varDecl;
} }
FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction) assembly::FunctionDefinition Parser::parseFunctionDefinition()
{ {
if (_instruction.type() != typeid(Instruction)) FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
fatalParserError("Assembly instruction required in front of \"(\")"); expectToken(Token::Function);
FunctionalInstruction ret; funDef.name = expectAsmIdentifier();
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
solidity::Instruction instr = ret.instruction.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
fatalParserError("DUPi instructions not allowed for functional notation");
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
fatalParserError("SWAPi instructions not allowed for functional notation");
expectToken(Token::LParen); expectToken(Token::LParen);
unsigned args = unsigned(instrInfo.args); while (m_scanner->currentToken() != Token::RParen)
for (unsigned i = 0; i < args; ++i)
{ {
ret.arguments.emplace_back(parseExpression()); funDef.arguments.push_back(expectAsmIdentifier());
if (i != args - 1) if (m_scanner->currentToken() == Token::RParen)
{ break;
if (m_scanner->currentToken() != Token::Comma) expectToken(Token::Comma);
fatalParserError(string(
"Expected comma (" +
instrInfo.name +
" expects " +
boost::lexical_cast<string>(args) +
" arguments)"
));
else
m_scanner->next();
}
} }
ret.location.end = endPosition();
if (m_scanner->currentToken() == Token::Comma)
fatalParserError(
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
);
expectToken(Token::RParen); expectToken(Token::RParen);
return ret; if (m_scanner->currentToken() == Token::Sub)
{
expectToken(Token::Sub);
expectToken(Token::GreaterThan);
expectToken(Token::LParen);
while (true)
{
funDef.returns.push_back(expectAsmIdentifier());
if (m_scanner->currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
expectToken(Token::RParen);
}
funDef.body = parseBlock();
funDef.location.end = funDef.body.location.end;
return funDef;
}
assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
{
if (_instruction.type() == typeid(Instruction))
{
FunctionalInstruction ret;
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
solidity::Instruction instr = ret.instruction.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
fatalParserError("DUPi instructions not allowed for functional notation");
if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
fatalParserError("SWAPi instructions not allowed for functional notation");
expectToken(Token::LParen);
unsigned args = unsigned(instrInfo.args);
for (unsigned i = 0; i < args; ++i)
{
ret.arguments.emplace_back(parseExpression());
if (i != args - 1)
{
if (m_scanner->currentToken() != Token::Comma)
fatalParserError(string(
"Expected comma (" +
instrInfo.name +
" expects " +
boost::lexical_cast<string>(args) +
" arguments)"
));
else
m_scanner->next();
}
}
ret.location.end = endPosition();
if (m_scanner->currentToken() == Token::Comma)
fatalParserError(
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
);
expectToken(Token::RParen);
return ret;
}
else if (_instruction.type() == typeid(Identifier))
{
FunctionCall ret;
ret.functionName = std::move(boost::get<Identifier>(_instruction));
ret.location = ret.functionName.location;
expectToken(Token::LParen);
while (m_scanner->currentToken() != Token::RParen)
{
ret.arguments.emplace_back(parseExpression());
if (m_scanner->currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
ret.location.end = endPosition();
expectToken(Token::RParen);
return ret;
}
else
fatalParserError("Assembly instruction or function name required in front of \"(\")");
return {};
}
string Parser::expectAsmIdentifier()
{
string name = m_scanner->currentLiteral();
if (instructions().count(name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
return name;
} }

View File

@ -67,7 +67,9 @@ protected:
std::map<std::string, dev::solidity::Instruction> const& instructions(); std::map<std::string, dev::solidity::Instruction> const& instructions();
Statement parseElementaryOperation(bool _onlySinglePusher = false); Statement parseElementaryOperation(bool _onlySinglePusher = false);
VariableDeclaration parseVariableDeclaration(); VariableDeclaration parseVariableDeclaration();
FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); FunctionDefinition parseFunctionDefinition();
Statement parseFunctionalInstruction(Statement&& _instruction);
std::string expectAsmIdentifier();
}; };
} }

View File

@ -0,0 +1,143 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2017
* Converts a parsed assembly into its textual form.
*/
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <memory>
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
//@TODO source locations
string AsmPrinter::operator()(assembly::Instruction const& _instruction)
{
return boost::to_lower_copy(instructionInfo(_instruction.instruction).name);
}
string AsmPrinter::operator()(assembly::Literal const& _literal)
{
if (_literal.isNumber)
return _literal.value;
string out;
for (char c: _literal.value)
if (c == '\\')
out += "\\\\";
else if (c == '"')
out += "\\\"";
else if (c == '\b')
out += "\\b";
else if (c == '\f')
out += "\\f";
else if (c == '\n')
out += "\\n";
else if (c == '\r')
out += "\\r";
else if (c == '\t')
out += "\\t";
else if (c == '\v')
out += "\\v";
else if (!isprint(c, locale::classic()))
{
ostringstream o;
o << std::hex << setfill('0') << setw(2) << (unsigned)(unsigned char)(c);
out += "\\x" + o.str();
}
else
out += c;
return "\"" + out + "\"";
}
string AsmPrinter::operator()(assembly::Identifier const& _identifier)
{
return _identifier.name;
}
string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction)
{
return
(*this)(_functionalInstruction.instruction) +
"(" +
boost::algorithm::join(
_functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
", " ) +
")";
}
string AsmPrinter::operator()(assembly::Label const& _label)
{
return _label.name + ":";
}
string AsmPrinter::operator()(assembly::Assignment const& _assignment)
{
return "=: " + (*this)(_assignment.variableName);
}
string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalAssignment)
{
return (*this)(_functionalAssignment.variableName) + " := " + boost::apply_visitor(*this, *_functionalAssignment.value);
}
string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration)
{
return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value);
}
string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition)
{
string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
if (!_functionDefinition.returns.empty())
out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")";
return out + "\n" + (*this)(_functionDefinition.body);
}
string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall)
{
return
(*this)(_functionCall.functionName) + "(" +
boost::algorithm::join(
_functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
", " ) +
")";
}
string AsmPrinter::operator()(Block const& _block)
{
if (_block.statements.empty())
return "{\n}";
string body = boost::algorithm::join(
_block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)),
"\n"
);
boost::replace_all(body, "\n", "\n ");
return "{\n " + body + "\n}";
}

View File

@ -0,0 +1,63 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2017
* Converts a parsed assembly into its textual form.
*/
#pragma once
#include <boost/variant.hpp>
namespace dev
{
namespace solidity
{
namespace assembly
{
struct Instruction;
struct Literal;
struct Identifier;
struct FunctionalInstruction;
struct Label;
struct Assignment;
struct FunctionalAssignment;
struct VariableDeclaration;
struct FunctionDefinition;
struct FunctionCall;
struct Block;
class AsmPrinter: public boost::static_visitor<std::string>
{
public:
std::string operator()(assembly::Instruction const& _instruction);
std::string operator()(assembly::Literal const& _literal);
std::string operator()(assembly::Identifier const& _identifier);
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
std::string operator()(assembly::Label const& _label);
std::string operator()(assembly::Assignment const& _assignment);
std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment);
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
std::string operator()(assembly::FunctionDefinition const& _functionDefinition);
std::string operator()(assembly::FunctionCall const& _functionCall);
std::string operator()(assembly::Block const& _block);
};
}
}
}

View File

@ -21,12 +21,18 @@
*/ */
#include <libsolidity/inlineasm/AsmStack.h> #include <libsolidity/inlineasm/AsmStack.h>
#include <memory>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmCodeGen.h> #include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/parsing/Scanner.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <memory>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -40,8 +46,15 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
auto result = parser.parse(_scanner); auto result = parser.parse(_scanner);
if (!result) if (!result)
return false; return false;
*m_parserResult = std::move(*result); *m_parserResult = std::move(*result);
return true; AsmAnalyzer::Scopes scopes;
return (AsmAnalyzer(scopes, m_errors))(*m_parserResult);
}
string InlineAssemblyStack::toString()
{
return AsmPrinter()(*m_parserResult);
} }
eth::Assembly InlineAssemblyStack::assemble() eth::Assembly InlineAssemblyStack::assemble()

View File

@ -46,6 +46,10 @@ public:
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`. /// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
/// @return false or error. /// @return false or error.
bool parse(std::shared_ptr<Scanner> const& _scanner); bool parse(std::shared_ptr<Scanner> const& _scanner);
/// Converts the parser result back into a string form (not necessarily the same form
/// as the source form, but it should parse into the same parsed form again).
std::string toString();
eth::Assembly assemble(); eth::Assembly assemble();
/// Parse and assemble a string in one run - for use in Solidity code generation itself. /// Parse and assemble a string in one run - for use in Solidity code generation itself.

View File

@ -34,6 +34,7 @@
#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h> #include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/InterfaceHandler.h> #include <libsolidity/interface/InterfaceHandler.h>
@ -88,6 +89,7 @@ void CompilerStack::reset(bool _keepSources)
m_optimize = false; m_optimize = false;
m_optimizeRuns = 200; m_optimizeRuns = 200;
m_globalContext.reset(); m_globalContext.reset();
m_scopes.clear();
m_sourceOrder.clear(); m_sourceOrder.clear();
m_contracts.clear(); m_contracts.clear();
m_errors.clear(); m_errors.clear();
@ -165,7 +167,7 @@ bool CompilerStack::parse()
noErrors = false; noErrors = false;
m_globalContext = make_shared<GlobalContext>(); m_globalContext = make_shared<GlobalContext>();
NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors); NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errors);
for (Source const* source: m_sourceOrder) for (Source const* source: m_sourceOrder)
if (!resolver.registerDeclarations(*source->ast)) if (!resolver.registerDeclarations(*source->ast))
return false; return false;
@ -216,6 +218,14 @@ bool CompilerStack::parse()
m_contracts[contract->fullyQualifiedName()].contract = contract; m_contracts[contract->fullyQualifiedName()].contract = contract;
} }
if (noErrors)
{
PostTypeChecker postTypeChecker(m_errors);
for (Source const* source: m_sourceOrder)
if (!postTypeChecker.check(*source->ast))
noErrors = false;
}
if (noErrors) if (noErrors)
{ {
StaticAnalyzer staticAnalyzer(m_errors); StaticAnalyzer staticAnalyzer(m_errors);
@ -627,7 +637,7 @@ void CompilerStack::compileContract(
if ( if (
_compiledContracts.count(&_contract) || _compiledContracts.count(&_contract) ||
!_contract.annotation().isFullyImplemented || !_contract.annotation().isFullyImplemented ||
!_contract.annotation().hasPublicConstructor !_contract.constructorIsPublic()
) )
return; return;
for (auto const* dependency: _contract.annotation().contractDependencies) for (auto const* dependency: _contract.annotation().contractDependencies)

View File

@ -52,6 +52,7 @@ namespace solidity
// forward declarations // forward declarations
class Scanner; class Scanner;
class ASTNode;
class ContractDefinition; class ContractDefinition;
class FunctionDefinition; class FunctionDefinition;
class SourceUnit; class SourceUnit;
@ -59,6 +60,7 @@ class Compiler;
class GlobalContext; class GlobalContext;
class InterfaceHandler; class InterfaceHandler;
class Error; class Error;
class DeclarationContainer;
enum class DocumentationType: uint8_t enum class DocumentationType: uint8_t
{ {
@ -271,6 +273,7 @@ private:
bool m_parseSuccessful; bool m_parseSuccessful;
std::map<std::string const, Source> m_sources; std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::vector<Source const*> m_sourceOrder; std::vector<Source const*> m_sourceOrder;
std::map<std::string const, Contract> m_contracts; std::map<std::string const, Contract> m_contracts;
std::string m_formalTranslation; std::string m_formalTranslation;

View File

@ -23,10 +23,12 @@
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/Utils.h> #include <libsolidity/interface/Utils.h>
using namespace std;
using namespace dev; using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
Error::Error(Type _type): m_type(_type) Error::Error(Type _type, SourceLocation const& _location, string const& _description):
m_type(_type)
{ {
switch(m_type) switch(m_type)
{ {
@ -55,4 +57,30 @@ Error::Error(Type _type): m_type(_type)
solAssert(false, ""); solAssert(false, "");
break; break;
} }
if (!_location.isEmpty())
*this << errinfo_sourceLocation(_location);
if (!_description.empty())
*this << errinfo_comment(_description);
}
Error::Error(Error::Type _type, const std::string& _description, const SourceLocation& _location):
Error(_type)
{
if (!_location.isEmpty())
*this << errinfo_sourceLocation(_location);
*this << errinfo_comment(_description);
}
string Exception::lineInfo() const
{
char const* const* file = boost::get_error_info<boost::throw_file>(*this);
int const* line = boost::get_error_info<boost::throw_line>(*this);
string ret;
if (file)
ret += *file;
ret += ':';
if (line)
ret += boost::lexical_cast<string>(*line);
return ret;
} }

View File

@ -53,7 +53,13 @@ public:
Warning Warning
}; };
explicit Error(Type _type); explicit Error(
Type _type,
SourceLocation const& _location = SourceLocation(),
std::string const& _description = std::string()
);
Error(Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation());
Type type() const { return m_type; } Type type() const { return m_type; }
std::string const& typeName() const { return m_typeName; } std::string const& typeName() const { return m_typeName; }

View File

@ -758,6 +758,9 @@ Token::Value Scanner::scanNumber(char _charSeen)
while (isHexDigit(m_char)) while (isHexDigit(m_char))
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
} }
else if (isDecimalDigit(m_char))
// We do not allow octal numbers
return Token::Illegal;
} }
// Parse decimal digits and allow trailing fractional part. // Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL) if (kind == DECIMAL)

View File

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of solidity.
cpp-ethereum is free software: you can redistribute it and/or modify solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file main.cpp /** @file main.cpp
* @author Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>

View File

@ -1,12 +1,16 @@
FROM alpine FROM alpine
MAINTAINER chriseth <chris@ethereum.org> MAINTAINER chriseth <chris@ethereum.org>
#Official solidity docker image
RUN \ #Establish working directory as solidity
apk --no-cache --update add build-base cmake boost-dev git && \ WORKDIR /solidity
sed -i -E -e 's/include <sys\/poll.h>/include <poll.h>/' /usr/include/boost/asio/detail/socket_types.hpp && \ #Copy working directory on travis to the image
git clone --depth 1 --recursive -b release https://github.com/ethereum/solidity && \ COPY / $WORKDIR
cd /solidity && cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSTATIC_LINKING=1 && \
cd /solidity && make solc && install -s solc/solc /usr/bin && \ #Install dependencies, eliminate annoying warnings, and build release, delete all remaining points and statically link.
cd / && rm -rf solidity && \ RUN ./scripts/install_deps.sh && sed -i -E -e 's/include <sys\/poll.h>/include <poll.h>/' /usr/include/boost/asio/detail/socket_types.hpp &&\
apk del sed build-base git make cmake gcc g++ musl-dev curl-dev boost-dev && \ cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSTATIC_LINKING=1 &&\
rm -rf /var/cache/apk/* make solc && install -s solc/solc /usr/bin &&\
cd / && rm -rf solidity &&\
apk del sed build-base git make cmake gcc g++ musl-dev curl-dev boost-dev &&\
rm -rf /var/cache/apk/*

23
scripts/build.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
if [ -z "$1" ]; then
BUILD_TYPE=Release
else
BUILD_TYPE="$1"
fi
cd $(dirname "$0")/.. &&
mkdir -p build &&
cd build &&
cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" &&
make -j2
if [ $? -ne 0 ]; then
echo "Failed to build"
exit 1
fi
if [ -z $CI ]; then
echo "Installing solc and soltest"
install solc/solc /usr/local/bin && install test/soltest /usr/local/bin
fi

23
scripts/docker_deploy.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env sh
set -e
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
version=$(grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")"? $(dirname "$0")/../CMakeLists.txt)
if [ "$TRAVIS_BRANCH" = "develop" ]
then
docker tag ethereum/solc:build ethereum/solc:nightly;
docker tag ethereum/solc:build ethereum/solc:nightly-"$version"-"$TRAVIS_COMMIT"
docker push ethereum/solc:nightly-"$version"-"$TRAVIS_COMMIT";
docker push ethereum/solc:nightly;
elif [ "$TRAVIS_BRANCH" = "release" ]
then
docker tag ethereum/solc:build ethereum/solc:stable;
docker push ethereum/solc:stable;
elif [ "$TRAVIS_TAG" = v"$version" ]
then
docker tag ethereum/solc:build ethereum/solc:"$version";
docker push ethereum/solc:"$version";
else
echo "Not publishing docker image from branch $TRAVIS_BRANCH or tag $TRAVIS_TAG"
fi

View File

@ -57,7 +57,7 @@ detect_linux_distro() {
# extract 'foo' from NAME=foo, only on the line with NAME=foo # extract 'foo' from NAME=foo, only on the line with NAME=foo
DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release) DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release)
elif [ -f /etc/centos-release ]; then elif [ -f /etc/centos-release ]; then
DISTRO=CentOS DISTRO=CentOS
else else
DISTRO='' DISTRO=''
fi fi
@ -93,19 +93,17 @@ case $(uname -s) in
# Check for Homebrew install and abort if it is not installed. # Check for Homebrew install and abort if it is not installed.
brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; } brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; }
brew update brew update
brew upgrade
brew install boost brew install boost
brew install cmake brew install cmake
if ["$CI" = true]; then
# We should really 'brew install' our eth client here, but at the time of writing brew upgrade cmake
# the bottle is known broken, so we will just cheat and use a hardcoded ZIP for brew tap ethereum/ethereum
# the time being, which is good enough. The cause of the breaks will go away brew install cpp-ethereum
# when we commit the repository reorg changes anyway. brew linkapps cpp-ethereum
curl -L -O https://github.com/bobsummerwill/cpp-ethereum/releases/download/v1.3.0/cpp-ethereum-osx-mavericks-v1.3.0.zip else
unzip cpp-ethereum-osx-mavericks-v1.3.0.zip brew upgrade
fi
;; ;;
@ -207,7 +205,6 @@ case $(uname -s) in
# Install "normal packages" # Install "normal packages"
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install \ sudo apt-get -y install \
python-sphinx \
build-essential \ build-essential \
cmake \ cmake \
g++ \ g++ \
@ -311,17 +308,17 @@ case $(uname -s) in
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install \ sudo apt-get -y install \
python-sphinx \
build-essential \ build-essential \
cmake \ cmake \
git \ git \
libboost-all-dev libboost-all-dev
if [ "$CI" = true ]; then
# Install 'eth', for use in the Solidity Tests-over-IPC. # Install 'eth', for use in the Solidity Tests-over-IPC.
sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install eth sudo apt-get -y install eth
fi
;; ;;
@ -397,4 +394,4 @@ case $(uname -s) in
echo "If you would like to get your operating system working, that would be fantastic." echo "If you would like to get your operating system working, that would be fantastic."
echo "Drop us a message at https://gitter.im/ethereum/solidity." echo "Drop us a message at https://gitter.im/ethereum/solidity."
;; ;;
esac esac

View File

@ -35,7 +35,7 @@ echo "Running commandline tests..."
echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..." echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..."
output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l) output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l)
test "$output" = "3" test "${output//[[:blank:]]/}" = "3"
# This conditional is only needed because we don't have a working Homebrew # This conditional is only needed because we don't have a working Homebrew
# install for `eth` at the time of writing, so we unzip the ZIP file locally # install for `eth` at the time of writing, so we unzip the ZIP file locally
@ -52,21 +52,22 @@ fi
# true and continue as normal, either processing further commands in a script # true and continue as normal, either processing further commands in a script
# or returning the cursor focus back to the user in a Linux terminal. # or returning the cursor focus back to the user in a Linux terminal.
$ETH_PATH --test -d /tmp/test & $ETH_PATH --test -d /tmp/test &
ETH_PID=$!
# Wait until the IPC endpoint is available. That won't be available instantly. # Wait until the IPC endpoint is available. That won't be available instantly.
# The node needs to get a little way into its startup sequence before the IPC # The node needs to get a little way into its startup sequence before the IPC
# is available and is ready for the unit-tests to start talking to it. # is available and is ready for the unit-tests to start talking to it.
while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done
echo "--> IPC available." echo "--> IPC available."
sleep 2
# And then run the Solidity unit-tests (once without optimization, once with), # And then run the Solidity unit-tests (once without optimization, once with),
# pointing to that IPC endpoint. # pointing to that IPC endpoint.
echo "--> Running tests without optimizer..." echo "--> Running tests without optimizer..."
"$REPO_ROOT"/build/test/soltest -- --ipcpath /tmp/test/geth.ipc && \ "$REPO_ROOT"/build/test/soltest --show-progress -- --ipcpath /tmp/test/geth.ipc && \
echo "--> Running tests WITH optimizer..." && \ echo "--> Running tests WITH optimizer..." && \
"$REPO_ROOT"/build/test/soltest -- --optimize --ipcpath /tmp/test/geth.ipc "$REPO_ROOT"/build/test/soltest --show-progress -- --optimize --ipcpath /tmp/test/geth.ipc
ERROR_CODE=$? ERROR_CODE=$?
pkill eth || true pkill "$ETH_PID" || true
sleep 4 sleep 4
pgrep eth && pkill -9 eth || true pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
exit $ERROR_CODE exit $ERROR_CODE

Some files were not shown because too many files have changed in this diff Show More