Merge pull request #5571 from ethereum/develop

Version 0.5.1
This commit is contained in:
chriseth 2018-12-03 15:48:03 +01:00 committed by GitHub
commit c8a2cb6283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
425 changed files with 7160 additions and 4596 deletions

View File

@ -14,6 +14,7 @@ defaults:
command: |
mkdir -p build
cd build
[ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON"
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS
make -j4
- run_tests: &run_tests
@ -53,7 +54,7 @@ jobs:
name: Save Boost build
key: *boost-cache-key
paths:
- boost_1_57_0
- boost_1_67_0
- store_artifacts:
path: build/libsolc/soljson.js
destination: soljson.js
@ -122,7 +123,7 @@ jobs:
- image: buildpack-deps:artful
environment:
TERM: xterm
CMAKE_OPTIONS: -DCOVERAGE=OFF
COVERAGE: "ON"
steps:
- checkout
- run:
@ -139,6 +140,27 @@ jobs:
paths:
- "*"
build_x86_archlinux:
docker:
- image: archlinux/base
environment:
TERM: xterm
steps:
- run:
name: Install build dependencies
command: |
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar
- checkout
- run: *setup_prerelease_commit_hash
- run: *run_build
- store_artifacts: *solc_artifact
- persist_to_workspace:
root: build
paths:
- solc/solc
- test/soltest
- test/tools/solfuzzer
build_x86_clang7:
docker:
- image: buildpack-deps:cosmic
@ -146,6 +168,7 @@ jobs:
TERM: xterm
CC: /usr/bin/clang-7
CXX: /usr/bin/clang++-7
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run:
@ -167,6 +190,7 @@ jobs:
xcode: "10.0.0"
environment:
TERM: xterm
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run:
@ -260,6 +284,27 @@ jobs:
path: test_results/
destination: test_results/
test_x86_archlinux:
docker:
- image: archlinux/base
environment:
TERM: xterm
steps:
- run:
name: Install dependencies
command: |
pacman --noconfirm -Syu --noprogressbar --needed boost z3 cvc4 git openssh tar
- checkout
- attach_workspace:
at: build
- run: mkdir -p test_results
- run: build/test/soltest --logger=JUNIT,test_suite,test_results/result.xml -- --no-ipc --testpath test
- store_test_results:
path: test_results/
- store_artifacts:
path: test_results/
destination: test_results/
test_x86_mac:
macos:
xcode: "10.0.0"
@ -330,3 +375,8 @@ workflows:
requires:
- build_x86_mac
- docs: *build_on_tags
- build_x86_archlinux: *build_on_tags
- test_x86_archlinux:
<<: *build_on_tags
requires:
- build_x86_archlinux

View File

@ -21,6 +21,7 @@ about: Bug reports about the Solidity Compiler.
## Environment
- Compiler version:
- Target EVM version (as per compiler settings):
- Framework/IDE (e.g. Truffle or Remix):
- EVM execution environment / backend / blockchain client:
- Operating system:

View File

@ -90,7 +90,7 @@ matrix:
before_install:
- nvm install 8
- nvm use 8
- docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit
- docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit
env:
- SOLC_EMSCRIPTEN=On
- SOLC_INSTALL_DEPS_TRAVIS=Off
@ -153,7 +153,7 @@ git:
cache:
ccache: true
directories:
- boost_1_57_0
- boost_1_67_0
- $HOME/.local
install:

View File

@ -8,12 +8,13 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.5.0")
set(PROJECT_VERSION "0.5.1")
project(solidity VERSION ${PROJECT_VERSION})
option(LLL "Build LLL" OFF)
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF)
option(INSTALL_LLLC "Include lllc executable in installation" OFF)
option(INSTALL_LLLC "Include lllc executable in installation" ${LLL})
# Setup cccache.
include(EthCcache)
@ -44,14 +45,18 @@ include(EthOptions)
configure_project(TESTS)
add_subdirectory(libdevcore)
add_subdirectory(liblangutil)
add_subdirectory(libevmasm)
add_subdirectory(libyul)
add_subdirectory(libsolidity)
add_subdirectory(libsolc)
if (NOT EMSCRIPTEN)
add_subdirectory(solc)
add_subdirectory(liblll)
add_subdirectory(lllc)
if (LLL)
add_subdirectory(liblll)
add_subdirectory(lllc)
endif()
endif()
if (TESTS AND NOT EMSCRIPTEN)

View File

@ -1,3 +1,44 @@
### 0.5.1 (2018-12-03)
Language Features:
* Allow mapping type for parameters and return variables of public and external library functions.
* Allow public functions to override external functions.
Compiler Features:
* Code generator: Do not perform redundant double cleanup on unsigned integers when loading from calldata.
* Commandline interface: Experimental ``--optimize`` option for assembly mode (``--strict-assembly`` and ``--yul``).
* SMTChecker: SMTLib2 queries and responses passed via standard JSON compiler interface.
* SMTChecker: Support ``msg``, ``tx`` and ``block`` member variables.
* SMTChecker: Support ``gasleft()`` and ``blockhash()`` functions.
* SMTChecker: Support internal bound function calls.
* Yul: Support Yul objects in ``--assemble``, ``--strict-assembly`` and ``--yul`` commandline options.
Bugfixes:
* Assembly output: Do not mix in/out jump annotations with arguments.
* Commandline interface: Fix crash when using ``--ast`` on empty runtime code.
* Code Generator: Annotate jump from calldata decoder to function as "jump in".
* Code Generator: Fix internal error related to state variables of function type access via base contract name.
* Optimizer: Fix nondeterminism bug related to the boost version and constants representation. The bug only resulted in less optimal but still correct code because the generated routine is always verified to be correct.
* Type Checker: Properly detect different return types when overriding an external interface function with a public contract function.
* Type Checker: Disallow struct return types for getters of public state variables unless the new ABI encoder is active.
* Type Checker: Fix internal compiler error when a field of a struct used as a parameter in a function type has a non-existent type.
* Type Checker: Disallow functions ``sha3`` and ``suicide`` also without a function call.
* Type Checker: Fix internal compiler error with ``super`` when base contract function is not implemented.
* Type Checker: Fixed internal error when trying to create abstract contract in some cases.
* Type Checker: Fixed internal error related to double declaration of events.
* Type Checker: Disallow inline arrays of mapping type.
* Type Checker: Consider abstract function to be implemented by public state variable.
Build System:
* CMake: LLL is not built anymore by default. Must configure it with CMake as `-DLLL=ON`.
* Docker: Includes both Scratch and Alpine images.
* Emscripten: Upgrade to Emscripten SDK 1.37.21 and boost 1.67.
Solc-Js:
* Fix handling of standard-json in the commandline executable.
* Remove support of nodejs 4.
### 0.5.0 (2018-11-13)
How to update your code:

View File

@ -62,5 +62,6 @@ if you want to help.
[@chriseth](https://github.com/chriseth)
## License
Solidity is licensed under [GNU General Public License v3.0](https://github.com/ethereum/solidity/blob/develop/LICENSE.txt)
Solidity is licensed under [GNU General Public License v3.0](LICENSE.txt)
Some third-party code has its [own licensing terms](cmake/templates/license.h.in).

View File

@ -67,7 +67,7 @@ jsoncpp:
license you like.
scanner/token:
The libsolidity/parsing/{scanner,token}.{h,cpp} files are dervied from
The liblangutil/{CharStream,Scanner,Token}.{h,cpp} files are dervied from
code originating from the V8 project licensed under the following terms:
Copyright 2006-2012, the V8 project authors. All rights reserved.

View File

@ -612,5 +612,9 @@
"0.5.0": {
"bugs": [],
"released": "2018-11-13"
},
"0.5.1": {
"bugs": [],
"released": "2018-12-03"
}
}

View File

@ -485,6 +485,110 @@ value types and strings.
Functions
*********
.. _function-parameters-return-variables:
Function Parameters and Return Variables
========================================
As in JavaScript, functions may take parameters as input. Unlike in JavaScript
and C, functions may also return an arbitrary number of values as output.
Function Parameters
-------------------
Function parameters are declared the same way as variables, and the name of
unused parameters can be omitted.
For example, if you want your contract to accept one kind of external call
with two integers, you would use something like::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
}
}
Function parameters can be used as any other local variable and they can also be assigned to.
.. note::
An :ref:`external function<external-function-calls>` cannot accept a
multi-dimensional array as an input
parameter. This functionality is possible if you enable the new
experimental ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file.
An :ref:`internal function<external-function-calls>` can accept a
multi-dimensional array without enabling the feature.
.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct
Return Variables
----------------
Function return variables are declared with the same syntax after the
``returns`` keyword.
For example, suppose you want to return two results: the sum and the product of
two integers passed as function parameters, then you use something like::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
o_sum = _a + _b;
o_product = _a * _b;
}
}
The names of return variables can be omitted.
Return variables can be used as any other local variable and they
are initialized with their :ref:`default value <default-value>` and have that value unless explicitly set.
You can either explicitly assign to return variables and
then leave the function using ``return;``,
or you can provide return values
(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return``
statement::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
return (_a + _b, _a * _b);
}
}
This form is equivalent to first assigning values to the
return variables and then using ``return;`` to leave the function.
.. note::
You cannot return some types from non-internal functions, notably
multi-dimensional dynamic arrays and structs. If you enable the
new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental
ABIEncoderV2;`` to your source file then more types are available, but
``mapping`` types are still limited to inside a single contract and you
cannot transfer them.
.. _multi-return:
Returning Multiple Values
-------------------------
When a function has multiple return types, the statement ``return (v0, v1, ..., vn) can be used to return multiple values.
vn)`` can return multiple values. The number of components must be
the same as the number of return types.
.. index:: ! view function, function;view
.. _view-functions:
@ -569,6 +673,20 @@ In addition to the list of state modifying statements explained above, the follo
}
}
Pure functions are able to use the `revert()` and `require()` functions to revert
potential state changes when an :ref:`error occurs <assert-and-require>`.
Reverting a state change is not considered a "state modification", as only changes to the
state made previously in code that did not have the ``view`` or ``pure`` restriction
are reverted and that code has the option to catch the ``revert`` and not pass it on.
This behaviour is also in line with the ``STATICCALL`` opcode.
.. warning::
It is not possible to prevent functions from reading the state at the level
of the EVM, it is only possible to prevent them from writing to the state
(i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
.. note::
Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode
for ``pure`` functions.
@ -577,13 +695,8 @@ In addition to the list of state modifying statements explained above, the follo
By using ``STATICCALL`` for ``pure`` functions, modifications to the
state are prevented on the level of the EVM.
.. warning::
It is not possible to prevent functions from reading the state at the level
of the EVM, it is only possible to prevent them from writing to the state
(i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
.. warning::
Before version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state.
.. note::
Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state.
It is a compile-time type check, which can be circumvented doing invalid explicit conversions
between contract types, because the compiler can verify that the type of the contract does
not do state-changing operations, but it cannot check that the contract that will be called
@ -932,14 +1045,15 @@ Additional Resources for Understanding Events
Inheritance
***********
Solidity supports multiple inheritance by copying code including polymorphism.
Solidity supports multiple inheritance including polymorphism.
All function calls are virtual, which means that the most derived function
is called, except when the contract name is explicitly given.
is called, except when the contract name is explicitly given or the
``super`` keyword is used.
When a contract inherits from other contracts, only a single
contract is created on the blockchain, and the code from all the base contracts
is copied into the created contract.
is compiled into the created contract.
The general inheritance system is very similar to
`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,

View File

@ -70,16 +70,15 @@ Thank you for your help!
Running the compiler tests
==========================
There is a script at ``scripts/tests.sh`` which executes most of the tests and
The ``./scripts/tests.sh`` script executes most Solidity tests and
runs ``aleth`` automatically if it is in the path, but does not download it,
so it most likely will not work right away. Please read on for the details.
so you need to install it first. Please read on for the details.
Solidity includes different types of tests. Most of them are bundled in the application
called ``soltest``. Some of them require the ``aleth`` client in testing mode,
some others require ``libz3`` to be installed.
Solidity includes different types of tests, most of them bundled into the ``soltest``
application. Some of them require the ``aleth`` client in testing mode, others require ``libz3``.
To run a basic set of tests that neither require ``aleth`` nor ``libz3``, run
``./scripts/soltest.sh --no-ipc --no-smt``. This script will run ``build/test/soltest``
To run a basic set of tests that require neither ``aleth`` nor ``libz3``, run
``./scripts/soltest.sh --no-ipc --no-smt``. This script runs ``./build/test/soltest``
internally.
.. note ::
@ -90,23 +89,41 @@ internally.
The option ``--no-smt`` disables the tests that require ``libz3`` and
``--no-ipc`` disables those that require ``aleth``.
If you want to run the ipc tests (those test the semantics of the generated code),
If you want to run the ipc tests (that test the semantics of the generated code),
you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it).
Then you run the actual tests: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
To run a subset of tests, filters can be used:
To run a subset of tests, you can use filters:
``./scripts/soltest.sh -t TestSuite/TestName --ipcpath /tmp/testeth/geth.ipc``,
where ``TestName`` can be a wildcard ``*``.
The script ``scripts/tests.sh`` also runs commandline tests and compilation tests
For example, here's an example test you might run;
``./scripts/soltest.sh -t "yulOptimizerTests/disambiguator/*" --no-ipc --no-smt``.
This will test all the tests for the disambiguator.
To get a list of all tests, use
``./build/test/soltest --list_content=HRF -- --ipcpath /tmp/irrelevant``.
If you want to debug using GDB, make sure you build differently than the "usual".
For example, you could run the following command in your ``build`` folder:
::
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
This will create symbols such that when you debug a test using the ``--debug`` flag, you will have acecess to functions and varialbes in which you can break or print with.
The script ``./scripts/tests.sh`` also runs commandline tests and compilation tests
in addition to those found in ``soltest``.
The CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
The CI runs additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
.. note ::
Some versions of ``aleth`` cannot be used for testing. We suggest using the same version that is used by the Solidity continuous integration tests.
You can not use some versions of ``aleth`` for testing. We suggest using
the same version that the Solidity continuous integration tests use.
Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``.
Writing and running syntax tests
@ -114,11 +131,11 @@ Writing and running syntax tests
Syntax tests check that the compiler generates the correct error messages for invalid code
and properly accepts valid code.
They are stored in individual files inside ``tests/libsolidity/syntaxTests``.
They are stored in individual files inside the ``tests/libsolidity/syntaxTests`` folder.
These files must contain annotations, stating the expected result(s) of the respective test.
The test suite will compile and check them against the given expectations.
The test suite compiles and checks them against the given expectations.
Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
For example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
::
@ -129,14 +146,14 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
// ----
// DeclarationError: (36-52): Identifier already declared.
A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The following comments are used to describe the
A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The comments that follow the separator are used to describe the
expected compiler errors or warnings. The number range denotes the location in the source where the error occurred.
In case the contract should compile without any errors or warning, the section after the separator has to be empty
and the separator can be left out completely.
If you want the contract to compile without any errors or warning you can leave
out the separator and the comments that follow it.
In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifier was already declared.
In the above example, the state variable ``variable`` was declared twice, which is not allowed. This results in a ``DeclarationError`` stating that the identifier was already declared.
The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows
The ``isoltest`` tool is used for these tests and you can find it under ``./build/test/tools/``. It is an interactive tool which allows
editing of failing contracts using your preferred text editor. Let's try to break this test by removing the second declaration of ``variable``:
::
@ -147,7 +164,7 @@ editing of failing contracts using your preferred text editor. Let's try to brea
// ----
// DeclarationError: (36-52): Identifier already declared.
Running ``./test/isoltest`` again will result in a test failure:
Running ``./build/test/isoltest`` again results in a test failure:
::
@ -163,15 +180,19 @@ Running ``./test/isoltest`` again will result in a test failure:
Success
``isoltest`` prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit.
``isoltest`` prints the expected result next to the obtained result, and also
provides a way to edit, update or skip the current contract file, or quit the application.
It offers several options for failing tests:
- edit: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in this order).
- update: Updates the contract under test. This either removes the annotation which contains the exception not met or adds missing expectations. The test will then be run again.
- skip: Skips the execution of this particular test.
- quit: Quits ``isoltest``.
- ``edit``: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in that order).
- ``update``: Updates the expectations for contract under test. This updates the annotations by removing unmet expectations and adding missing expectations. The test is then run again.
- ``skip``: Skips the execution of this particular test.
- ``quit``: Quits ``isoltest``.
Automatically updating the test above will change it to
All of these options apply to the current contract, expect ``quit`` which stops the entire testing process.
Automatically updating the test above changes it to
::
@ -180,7 +201,7 @@ Automatically updating the test above will change it to
}
// ----
and re-run the test. It will now pass again:
and re-run the test. It now passes again:
::
@ -190,7 +211,7 @@ and re-run the test. It will now pass again:
.. note::
Please choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``.
Choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``.
Do not put more than one contract into a single file, unless you are testing inheritance or cross-contract calls.
Each file should test one aspect of your new feature.

View File

@ -2,74 +2,8 @@
Expressions and Control Structures
##################################
.. index:: ! parameter, parameter;input, parameter;output, parameter;multiple
.. index:: ! parameter, parameter;input, parameter;output, function parameter, parameter;function, return variable, variable;return, return
Input Parameters and Output Parameters
======================================
As in Javascript, functions may take parameters as input;
unlike in Javascript and C, they may also return arbitrary number of
parameters as output.
Input Parameters
----------------
The input parameters are declared the same way as variables are.
The name of unused parameters can be omitted.
For example, suppose we want our contract to
accept one kind of external calls with two integers, we would write
something like::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
}
}
Input parameters can be used just as any other local variable
can be used, they can also be assigned to.
.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct
Output Parameters
-----------------
The output parameters can be declared with the same syntax after the
``returns`` keyword. For example, suppose we wished to return two results:
the sum and the product of the two given integers, then we would
write::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
o_sum = _a + _b;
o_product = _a * _b;
}
}
The names of output parameters can be omitted.
The return values can be specified using ``return`` statements,
which are also capable of :ref:`returning multiple values<multi-return>`.
Return parameters can be used as any other local variable and they
are zero-initialized; if they are not explicitly
set, they stay zero.
.. note::
You cannot return some types from non-internal functions, notably
multi-dimensional dynamic arrays and structs. If you enable the
new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental
ABIEncoderV2;`` to your source file then more types are available, but
``mapping`` types are still limited to inside a single contract and you
cannot transfer them.
.. index:: if, else, while, do/while, for, break, continue, return, switch, goto
@ -88,15 +22,6 @@ Note that there is no type conversion from non-boolean to boolean types as
there is in C and JavaScript, so ``if (1) { ... }`` is *not* valid
Solidity.
.. _multi-return:
Returning Multiple Values
-------------------------
When a function has multiple output parameters, ``return (v0, v1, ...,
vn)`` can return multiple values. The number of components must be
the same as the number of output parameters.
.. index:: ! function;call, function;internal, function;external
.. _function-calls:
@ -104,6 +29,8 @@ the same as the number of output parameters.
Function Calls
==============
.. _internal-function-calls:
Internal Function Calls
-----------------------
@ -125,6 +52,8 @@ contract can be called internally.
You should still avoid excessive recursion, as every internal function call
uses up at least one stack slot and there are at most 1024 slots available.
.. _external-function-calls:
External Function Calls
-----------------------

View File

@ -38,24 +38,6 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then
``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
examples.
Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];``
=========================================================================================
Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory
array in the return statement.
Example::
pragma solidity >=0.4.16 <0.6.0;
contract C {
function f() public pure returns (uint8[5] memory) {
string[4] memory adaArr = ["This", "is", "an", "array"];
adaArr[0] = "That";
return [1, 2, 3, 4, 5];
}
}
If I return an ``enum``, I only get integer values in web3.js. How to get the named values?
===========================================================================================
@ -143,44 +125,6 @@ arguments for you.
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and
`pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value?
============================================================================================================
This is not possible, because all types use up the full value range.
You have the option to ``throw`` on error, which will also revert the whole
transaction, which might be a good idea if you ran into an unexpected
situation.
If you do not want to throw, you can return a pair::
pragma solidity >0.4.23 <0.6.0;
contract C {
uint[] counters;
function getCounter(uint index)
public
view
returns (uint counter, bool error) {
if (index >= counters.length)
return (0, true);
else
return (counters[index], false);
}
function checkCounter(uint index) public view {
(uint counter, bool error) = getCounter(index);
if (error) {
// Handle the error
} else {
// Do something with counter.
require(counter > 7, "Invalid counter value");
}
}
}
Are comments included with deployed contracts and do they increase deployment gas?
==================================================================================
@ -232,24 +176,6 @@ Note2: Optimizing storage access can pull the gas costs down considerably, becau
currently do not work across loops and also have a problem with bounds checking.
You might get much better results in the future, though.
What happens to a ``struct``'s mapping when copying over a ``struct``?
======================================================================
This is a very interesting question. Suppose that we have a contract field set up like such::
struct User {
mapping(string => string) comments;
}
function somefunction public {
User user1;
user1.comments["Hello"] = "World";
User user2 = user1;
}
In this case, the mapping of the struct being copied over into ``user2`` is ignored as there is no "list of mapped keys".
Therefore it is not possible to find out which values should be copied over.
How do I initialize a contract with only a specific amount of wei?
==================================================================
@ -273,28 +199,6 @@ In this example::
}
}
What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean?
========================================================================================================================================================================
The type ``bytes32`` can hold 32 (raw) bytes. In the assignment ``bytes32 samevar = "stringliteral";``,
the string literal is interpreted in its raw byte form and if you inspect ``somevar`` and
see a 32-byte hex value, this is just ``"stringliteral"`` in hex.
The type ``bytes`` is similar, only that it can change its length.
Finally, ``string`` is basically identical to ``bytes`` only that it is assumed
to hold the UTF-8 encoding of a real string. Since ``string`` stores the
data in UTF-8 encoding it is quite expensive to compute the number of
characters in the string (the encoding of some characters takes more
than a single byte). Because of that, ``string s; s.length`` is not yet
supported and not even index access ``s[2]``. But if you want to access
the low-level byte encoding of the string, you can use
``bytes(s).length`` and ``bytes(s)[2]`` which will result in the number
of bytes in the UTF-8 encoding of the string (not the number of
characters) and the second byte (not character) of the UTF-8 encoded
string, respectively.
Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract?
=====================================================================================================
@ -325,55 +229,6 @@ to create an independent copy of the storage value in memory.
On the other hand, ``h(x)`` successfully modifies ``x`` because only
a reference and not a copy is passed.
Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why?
==================================================================================================================================================
You can resize a dynamic array in storage (i.e. an array declared at the
contract level) with ``arrayname.length = <some new length>;``. If you get the
"lvalue" error, you are probably doing one of two things wrong.
1. You might be trying to resize an array in "memory", or
2. You might be trying to resize a non-dynamic array.
::
pragma solidity >=0.4.18 <0.6.0;
// This will not compile
contract C {
int8[] dynamicStorageArray;
int8[5] fixedStorageArray;
function f() public {
int8[] memory memArr; // Case 1
memArr.length++; // illegal
int8[5] storage storageArr = fixedStorageArray; // Case 2
storageArr.length++; // illegal
int8[] storage storageArr2 = dynamicStorageArray;
storageArr2.length++; // legal
}
}
**Important note:** In Solidity, array dimensions are declared backwards from the way you
might be used to declaring them in C or Java, but they are access as in
C or Java.
For example, ``int8[][5] somearray;`` are 5 dynamic ``int8`` arrays.
The reason for this is that ``T[5]`` is always an array of 5 ``T``'s,
no matter whether ``T`` itself is an array or not (this is not the
case in C or Java).
Is it possible to return an array of strings (``string[]``) from a Solidity function?
=====================================================================================
Only when ``pragma experimental "ABIEncoderV2";`` is used.
What does the following strange check do in the Custom Token contract?
======================================================================

View File

@ -72,7 +72,7 @@ WhileStatement = 'while' '(' Expression ')' Statement
PlaceholderStatement = '_'
SimpleStatement = VariableDefinition | ExpressionStatement
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
InlineAssemblyStatement = 'assembly' StringLiteral? InlineAssemblyBlock
InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock
DoWhileStatement = 'do' Statement 'while' '(' Expression ')'
Continue = 'continue'
Break = 'break'
@ -152,11 +152,30 @@ Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ )
Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ )
InlineAssemblyBlock = '{' AssemblyItem* '}'
AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyVariableDeclaration | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral
AssemblyExpression = Identifier | FunctionalAssemblyExpression | NumberLiteral | StringLiteral | HexLiteral
AssemblyVariableDeclaration = 'let' Identifier ':=' AssemblyExpression
AssemblyAssignment = ( Identifier ':=' AssemblyExpression ) | ( '=:' Identifier )
AssemblyLabel = Identifier ':'
FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')'
AssemblyBlock = '{' AssemblyStatement* '}'
AssemblyStatement = AssemblyBlock
| AssemblyFunctionDefinition
| AssemblyVariableDeclaration
| AssemblyAssignment
| AssemblyIf
| AssemblyExpression
| AssemblySwitch
| AssemblyForLoop
| AssemblyBreakContinue
AssemblyFunctionDefinition =
'function' Identifier '(' AssemblyIdentifierList? ')'
( '->' AssemblyIdentifierList )? AssemblyBlock
AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )?
AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression
AssemblyExpression = AssemblyFunctionCall | Identifier | Literal
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
AssemblySwitch = 'switch' AssemblyExpression ( Case+ AssemblyDefault? | AssemblyDefault )
AssemblyCase = 'case' Literal AssemblyBlock
AssemblyDefault = 'default' AssemblyBlock
AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock
AssemblyBreakContinue = 'break' | 'continue'
AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
AssemblyIdentifierList = Identifier ( ',' Identifier )*

View File

@ -19,26 +19,43 @@ user-defined types among other features.
With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions,
and multi-signature wallets.
.. note::
The best way to try out Solidity right now is using
`Remix <https://remix.ethereum.org/>`_
(it can take a while to load, please be patient). Remix is a web browser
based IDE that allows you to write Solidity smart contracts, then deploy
and run the smart contracts.
Language Documentation
----------------------
If you are new to the concept of smart contracts we recommend you start with
:ref:`an example smart contract <simple-smart-contract>` written
in Solidity. When you are ready for more detail, we recommend you read the
:doc:`"Solidity by Example" <solidity-by-example>` and :doc:`"Solidity in Depth" <solidity-in-depth>` sections to learn the core concepts of the language.
For further reading, try :ref:`the basics of blockchains <blockchain-basics>`
and details of the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
.. hint::
You can always try out code examples in your browser with the
`Remix IDE <https://remix.ethereum.org>`_. Remix is a web browser based IDE
that allows you to write Solidity smart contracts, then deploy and run the
smart contracts. It can take a while to load, so please be patient.
.. warning::
Since software is written by humans, it can have bugs. Thus, also
smart contracts should be created following well-known best-practices in
software development. This includes code review, testing, audits and correctness proofs.
Also note that users are sometimes more confident in code than its authors.
Finally, blockchains have their own things to watch out for, so please take
a look at the section :ref:`security_considerations`.
As humans write software, it can have bugs. You should follow established
software development best-practices when writing your smart contracts, this
includes code review, testing, audits, and correctness proofs. Smart contract
users are sometimes more confident with code than their authors, and
blockchains and smart contracts have their own unique issues to
watch out for, so before working on production code, make sure you read the
:ref:`security_considerations` section.
If you have any questions, you can try searching for answers or asking on the
`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_, or our `gitter channel <https://gitter.im/ethereum/solidity/>`_.
Ideas for improving Solidity or this documentation are always welcome, read our :doc:`contributors guide <contributing>` for more details.
Translations
------------
This documentation is translated into several languages by community volunteers
with varying degrees of completeness and up-to-dateness. The English version stands as a reference.
Community volunteers help translate this documentation into several languages.
They have varying degrees of completeness and up-to-dateness. The English
version stands as a reference.
* `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress)
* `Spanish <https://solidity-es.readthedocs.io>`_
@ -46,25 +63,6 @@ with varying degrees of completeness and up-to-dateness. The English version sta
* `Korean <http://solidity-kr.readthedocs.io>`_ (in progress)
* `French <http://solidity-fr.readthedocs.io>`_ (in progress)
Language Documentation
----------------------
On the next pages, we will first see a :ref:`simple smart contract <simple-smart-contract>` written
in Solidity followed by the basics about :ref:`blockchains <blockchain-basics>`
and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
The next section will explain several *features* of Solidity by giving
useful :ref:`example contracts <voting>`.
Remember that you can always try out the contracts
`in your browser <https://remix.ethereum.org>`_!
The fourth and most extensive section will cover all aspects of Solidity in depth.
If you still have questions, you can try searching or asking on the
`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
site, or come to our `gitter channel <https://gitter.im/ethereum/solidity/>`_.
Ideas for improving Solidity or this documentation are always welcome!
Contents
========

View File

@ -115,7 +115,7 @@ Arch Linux also has packages, albeit limited to the latest development version:
pacman -S solidity
We distribute the Solidity compiler through Homebrow
We distribute the Solidity compiler through Homebrew
as a build-from-source version. Pre-built bottles are
currently not supported.

View File

@ -400,7 +400,7 @@ within a word). At the time of expansion, the cost in gas must be paid. Memory i
costly the larger it grows (it scales quadratically).
The EVM is not a register machine but a stack machine, so all
computations are performed on an data area called the **stack**. It has a maximum size of
computations are performed on a data area called the **stack**. It has a maximum size of
1024 elements and contains words of 256 bits. Access to the stack is
limited to the top end in the following way:
It is possible to copy one of

View File

@ -276,7 +276,7 @@ functions, annotate conditions for formal verification, and provide a
function.
In the following example we document the title of the contract, the explanation
for the two input parameters and two returned values.
for the two function parameters and two return variables.
::

View File

@ -9,6 +9,13 @@ LLL is a low-level language for the EVM with an s-expressions syntax.
The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity.
However, apart from maintaining that it still compiles, no other improvements are made to it.
It is not built unless specifically requested:
.. code-block:: bash
$ cmake -DLLL=ON ..
$ cmake --build .
.. warning::
The LLL codebase is deprecated and will be removed from the Solidity repository in the future.

View File

@ -8,13 +8,17 @@ Miscellaneous
Layout of State Variables in Storage
************************************
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
- The first item in a storage slot is stored lower-order aligned.
- Elementary types use only that many bytes that are necessary to store them.
- If an elementary type does not fit the remaining part of a storage slot, it is moved to the next storage slot.
- Structs and array data always start a new slot and occupy whole slots (but items inside a struct or array are packed tightly according to these rules).
For contracts that use inheritance, the ordering of state variables is determined by the
C3-linearized order of contracts starting with the most base-ward contract. If allowed
by the above rules, state variables from different contracts do share the same storage slot.
.. warning::
When using elements that are smaller than 32 bytes, your contract's gas usage may be higher.
This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller

View File

@ -21,9 +21,8 @@ Solidity Integrations
* Generic:
* `EthFiddle <https://ethfiddle.com/>`_
Solidity IDE in the Browser. Write and share your solidity code. Uses server-side components.
Solidity IDE in the Browser. Write and share your Solidity code. Uses server-side components.
* `Remix <https://remix.ethereum.org/>`_
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
@ -34,6 +33,9 @@ Solidity Integrations
* `Solhint <https://github.com/protofire/solhint>`_
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
* `Superblocks Lab <https://lab.superblocks.com/>`_
Browser-based IDE. Built-in browser-based VM and Metamask integration (one click deployment to Testnet/Mainnet).
* Atom:
* `Etheratom <https://github.com/0mkara/etheratom>`_

View File

@ -647,10 +647,10 @@ Safe Remote Purchase
Micropayment Channel
********************
In this section we will learn how to build a simple implementation
of a payment channel. It use cryptographics signatures to make
In this section we will learn how to build an example implementation
of a payment channel. It uses cryptographic signatures to make
repeated transfers of Ether between the same parties secure, instantaneous, and
without transaction fees. To do it we need to understand how to
without transaction fees. For the example, we need to understand how to
sign and verify signatures, and setup the payment channel.
Creating and verifying signatures
@ -658,88 +658,67 @@ Creating and verifying signatures
Imagine Alice wants to send a quantity of Ether to Bob, i.e.
Alice is the sender and the Bob is the recipient.
Alice only needs to send cryptographically signed messages off-chain
(e.g. via email) to Bob and it will be very similar to writing checks.
Signatures are used to authorize transactions,
and they are a general tool that is available to
smart contracts. Alice will build a simple
smart contract that lets her transmit Ether, but
in a unusual way, instead of calling a function herself
to initiate a payment, she will let Bob
do that, and therefore pay the transaction fee.
Alice only needs to send cryptographically signed messages off-chain
(e.g. via email) to Bob and it is similar to writing checks.
Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on Ethereum.
Alice will build a simple smart contract that lets her transmit Ether, but instead of calling a function herself
to initiate a payment, she will let Bob do that, and therefore pay the transaction fee.
The contract will work as follows:
1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made.
2. Alice authorizes a payment by signing a message with their private key.
2. Alice authorises a payment by signing a message with their private key.
3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret
(you will understand it later), and the mechanism for sending it does not matter.
(explained later), and the mechanism for sending it does not matter.
4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the
authenticity of the message and then releases the funds.
Creating the signature
----------------------
Alice does not need to interact with Ethereum network to
sign the transaction, the process is completely offline.
In this tutorial, we will sign messages in the browser
using ``web3.js`` and ``MetaMask``.
In particular, we will use the standard way described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline.
In this tutorial, we will sign messages in the browser using `web3.js <https://github.com/ethereum/web3.js>`_ and `MetaMask <https://metamask.io>`_, using the method described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
as it provides a number of other security benefits.
::
/// Hashing first makes things easier
var hash = web3.utils.sha3("message to sign");
web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); });
/// Hashing first makes a few things easier
var hash = web3.sha3("message to sign");
web3.personal.sign(hash, web3.eth.defaultAccount, function () {...});
Note that the ``web3.personal.sign`` prepends the length of the message to the signed data.
Since we hash first, the message will always be exactly 32 bytes long,
and thus this length prefix is always the same, making everything easier.
.. note::
The ``web3.eth.personal.sign`` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same.
What to Sign
------------
For a contract that fulfills payments, the signed message must include:
For a contract that fulfils payments, the signed message must include:
1. The recipient's address
2. The amount to be transferred
3. Protection against replay attacks
1. The recipient's address.
2. The amount to be transferred.
3. Protection against replay attacks.
A replay attack is when a signed message is reused to claim authorization for
a second action.
To avoid replay attacks we will use the same as in Ethereum transactions
To avoid replay attacks we use the same as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an
account.
The smart contract will check if a nonce is used multiple times.
The smart contract checks if a nonce is used multiple times.
There is another type of replay attacks, it occurs when the
owner deploys a ``ReceiverPays`` smart contract, performs some payments,
and then destroy the contract. Later, she decides to deploy the
``RecipientPays`` smart contract again, but the new contract does not
know the nonces used in the previous deployment, so the attacker
can use the old messages again.
Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the ``RecipientPays`` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again.
Alice can protect against it including
the contract's address in the message, and only
messages containing contract's address itself will be accepted.
This functionality can be found in the first two lines of the ``claimPayment()`` function in the full contract
at the end of this chapter.
Alice can protect against this attack by including the contract's address in the message, and only messages containing the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section.
Packing arguments
-----------------
Now that we have identified what information to include in the
signed message, we are ready to put the message together, hash it,
and sign it. For simplicity, we just concatenate the data.
The
`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides
a function called ``soliditySHA3`` that mimics the behavior
of Solidity's ``keccak256`` function applied to arguments encoded
using ``abi.encodePacked``.
Putting it all together, here is a JavaScript function that
creates the proper signature for the ``ReceiverPays`` example:
Now that we have identified what information to include in the signed message,
we are ready to put the message together, hash it, and sign it. For simplicity,
we concatenate the data. The `ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_
library provides a function called ``soliditySHA3`` that mimics the behaviour of
Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``.
Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example:
::
@ -748,46 +727,30 @@ creates the proper signature for the ``ReceiverPays`` example:
// nonce can be any unique number to prevent replay attacks
// contractAddress is used to prevent cross-contract replay attacks
function signPayment(recipient, amount, nonce, contractAddress, callback) {
var hash = "0x" + ethereumjs.ABI.soliditySHA3(
var hash = "0x" + abi.soliditySHA3(
["address", "uint256", "uint256", "address"],
[recipient, amount, nonce, contractAddress]
).toString("hex");
web3.personal.sign(hash, web3.eth.defaultAccount, callback);
web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback);
}
Recovering the Message Signer in Solidity
-----------------------------------------
In general, ECDSA signatures consist of two parameters, ``r`` and ``s``.
Signatures in Ethereum include a third parameter called ``v``, that can be used
to recover which account's private key was used to sign in the message,
the transaction's sender. Solidity provides a built-in function
`ecrecover <mathematical-and-cryptographic-functions>`_
that accepts a message along with the ``r``, ``s`` and ``v`` parameters and
returns the address that was used to sign the message.
In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatures in Ethereum include a third parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in function `ecrecover <mathematical-and-cryptographic-functions>`_ that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message.
Extracting the Signature Parameters
-----------------------------------
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``,
so the first step is splitting those parameters back out. It can be done on the client,
but doing it inside the smart contract means only one signature parameter
needs to be sent rather than three.
Splitting apart a byte array into component parts is a little messy.
We will use `inline assembly <assembly>`_ to do the job
in the ``splitSignature`` function (the third function in the full contract
at the end of this chapter).
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a messy, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
Computing the Message Hash
--------------------------
The smart contract needs to know exactly what parameters were signed,
and so it must recreate the message from the parameters and use that
for signature verification. The functions ``prefixed`` and
``recoverSigner`` do this and their use can be found in the
``claimPayment`` function.
The smart contract needs to know exactly what parameters were signed, and so it
must recreate the message from the parameters and use that for signature verification.
The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment`` function.
The full contract
-----------------
@ -861,41 +824,26 @@ The full contract
Writing a Simple Payment Channel
================================
Alice will now build a simple but complete implementation of a payment channel.
Payment channels use cryptographic signatures to make repeated transfers
of Ether securely, instantaneously, and without transaction fees.
Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of Ether securely, instantaneously, and without transaction fees.
What is a Payment Channel?
--------------------------
Payment channels allow participants to make repeated transfers of Ether without
using transactions. This means that the delays and fees associated with transactions
can be avoided. We are going to explore a simple unidirectional payment channel between
two parties (Alice and Bob). Using it involves three steps:
Payment channels allow participants to make repeated transfers of Ether without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps:
1. Alice funds a smart contract with Ether. This "opens" the payment channel.
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment.
3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender.
Note that only steps 1 and 3 require Ethereum transactions, step 2 means that
the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email).
This means only two transactions are required to support any number of transfers.
.. note::
Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers.
Bob is guaranteed to receive their funds because the smart contract escrows
the Ether and honors a valid signed message. The smart contract also enforces a timeout,
so Alice is guaranteed to eventually recover their funds even if the recipient refuses
to close the channel.
It is up to the participants in a payment channel to decide how long to keep it open.
For a short-lived transaction, such as paying an internet cafe for each minute of network access,
or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years.
Bob is guaranteed to receive their funds because the smart contract escrows the Ether and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years.
Opening the Payment Channel
---------------------------
To open the payment channel, Alice deploys the smart contract,
attaching the Ether to be escrowed and specifying the intendend recipient
and a maximum duration for the channel to exist. It is the function
``SimplePaymentChannel`` in the contract, that is at the end of this chapter.
To open the payment channel, Alice deploys the smart contract, attaching the Ether to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function ``SimplePaymentChannel`` in the contract, at the end of this section.
Making Payments
---------------
@ -910,27 +858,26 @@ Each message includes the following information:
* The total amount of Ether that is owed the recipient so far.
A payment channel is closed just once, at the end of a series of transfers.
Because of this, only one of the messages sent will be redeemed. This is why
Because of this, only one of the messages sent is redeemed. This is why
each message specifies a cumulative total amount of Ether owed, rather than the
amount of the individual micropayment. The recipient will naturally choose to
redeem the most recent message because that is the one with the highest total.
The nonce per-message is not needed anymore, because the smart contract will
only honor a single message. The address of the smart contract is still used
The nonce per-message is not needed anymore, because the smart contract only honors a single message. The address of the smart contract is still used
to prevent a message intended for one payment channel from being used for a different channel.
Here is the modified javascript code to cryptographically sign a message from the previous chapter:
Here is the modified JavaScript code to cryptographically sign a message from the previous section:
::
function constructPaymentMessage(contractAddress, amount) {
return ethereumjs.ABI.soliditySHA3(
return abi.soliditySHA3(
["address", "uint256"],
[contractAddress, amount]
);
}
function signMessage(message, callback) {
web3.personal.sign(
web3.eth.personal.sign(
"0x" + message.toString("hex"),
web3.eth.defaultAccount,
callback
@ -951,18 +898,15 @@ Closing the Payment Channel
When Bob is ready to receive their funds, it is time to
close the payment channel by calling a ``close`` function on the smart contract.
Closing the channel pays the recipient the Ether they are owed and destroys the contract,
sending any remaining Ether back to Alice.
To close the channel, Bob needs to provide a message signed by Alice.
Closing the channel pays the recipient the Ether they are owed and destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice.
The smart contract must verify that the message contains a valid signature from the sender.
The process for doing this verification is the same as the process the recipient uses.
The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their
JavaScript counterparts in the previous section. The latter is borrowed from the
``ReceiverPays`` contract in the previous chapter.
JavaScript counterparts in the previous section, with the latter function borrowed from the ``ReceiverPays`` contract.
The ``close`` function can only be called by the payment channel recipient,
who will naturally pass the most recent payment message because that message
Only the payment channel recipient can call the ``close`` function,
who naturally passes the most recent payment message because that message
carries the highest total owed. If the sender were allowed to call this function,
they could provide a message with a lower amount and cheat the recipient out of what they are owed.
@ -977,13 +921,11 @@ Channel Expiration
Bob can close the payment channel at any time, but if they fail to do so,
Alice needs a way to recover their escrowed funds. An *expiration* time was set
at the time of contract deployment. Once that time is reached, Alice can call
``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the
full contract.
``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the full contract.
After this function is called, Bob can no longer receive any Ether,
so it is important that Bob closes the channel before the expiration is reached.
The full contract
-----------------
@ -1081,16 +1023,15 @@ The full contract
}
Note: The function ``splitSignature`` is very simple and does not use all security checks.
A real implementation should use a more rigorously tested library, such as
openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
.. note::
The function ``splitSignature`` does not use all security
checks. A real implementation should use a more rigorously tested library,
such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
Verifying Payments
------------------
Unlike in our previous chapter, messages in a payment channel aren't
Unlike in the previous section, messages in a payment channel aren't
redeemed right away. The recipient keeps track of the latest message and
redeems it when it's time to close the payment channel. This means it's
critical that the recipient perform their own verification of each message.
@ -1105,10 +1046,8 @@ The recipient should verify each message using the following process:
4. Verify that the signature is valid and comes from the payment channel sender.
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
library to write this verifications. The final step can be done a number of ways,
but if it's being done in **JavaScript**.
The following code borrows the `constructMessage` function from the signing **JavaScript code**
above:
library to write this verification. The final step can be done a number of ways,
and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above:
::

View File

@ -56,7 +56,8 @@ Functions are the executable units of code within a contract.
:ref:`function-calls` can happen internally or externally
and have different levels of :ref:`visibility<visibility-and-getters>`
towards other contracts.
towards other contracts. :ref:`Functions<functions>` accept :ref:`parameters and return variables<function-parameters-return-variables>` to pass parameters
and values between them.
.. _structure-function-modifiers:

View File

@ -809,7 +809,23 @@ possible permutations for function declarations.
Mappings
========
TODO
In variable declarations, do not separate the keyword ``mapping`` from its
type by a space. Do not separate any nested ``mapping`` keyword from its type by
whitespace.
Yes::
mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
No::
mapping (uint => uint) map;
mapping( address => bool ) registeredAddresses;
mapping (uint => mapping (bool => Data[])) public data;
mapping(uint => mapping (uint => s)) data;
Variable Declarations
=====================

View File

@ -13,6 +13,11 @@ Solidity provides several elementary types which can be combined to form complex
In addition, types can interact with each other in expressions containing
operators. For a quick reference of the various operators, see :ref:`order`.
The concept of "undefined" or "null" values does not exist in Solidity, but newly
declared variables always have a :ref:`default value<default-value>` dependent
on its type. To handle any unexpected values, you should use the :ref:`revert function<assert-and-require>` to revert the whole transaction, or return a
tuple with a second `bool` value denoting success.
.. index:: ! value type, ! type;value
Value Types
@ -253,7 +258,7 @@ Send is the low-level counterpart of ``transfer``. If the execution fails, the c
In order to interface with contracts that do not adhere to the ABI,
or to get more direct control over the encoding,
the functions ``call``, ``delegatecall`` and ``staticcall`` are provided.
They all take a single ``bytes memory`` argument as input and
They all take a single ``bytes memory`` parameter and
return the success condition (as a ``bool``) and the returned data
(``bytes memory``).
The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector``
@ -273,22 +278,22 @@ Example::
when the call returns. The regular way to interact with other contracts
is to call a function on a contract object (``x.f()``).
:: note::
.. note::
Previous versions of Solidity allowed these functions to receive
arbitrary arguments and would also handle a first argument of type
``bytes4`` differently. These edge cases were removed in version 0.5.0.
It is possible to adjust the supplied gas with the ``.gas()`` modifier::
namReg.call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
Similarly, the supplied Ether value can be controlled too::
nameReg.call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
Lastly, these modifiers can be combined. Their order does not matter::
nameReg.call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
@ -460,11 +465,13 @@ a non-rational number).
.. index:: literal, literal;string, string
.. _string_literals:
String Literals
---------------
String Literals and Types
-------------------------
String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``.
For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type.
String literals support the following escape characters:
- ``\<newline>`` (escapes an actual newline)
@ -857,13 +864,20 @@ or create a new memory array and copy every element.
}
}
.. index:: ! array;literals, !inline;arrays
.. index:: ! array;literals, ! inline;arrays
Array Literals / Inline Arrays
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Array Literals
^^^^^^^^^^^^^^
Array literals are arrays that are written as an expression and are not
assigned to a variable right away.
An array literal is a comma-separated list of one or more expressions, enclosed
in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a
common type all elements can be implicitly converted to. This is the elementary
type of the array.
Array literals are always statically-sized memory arrays.
In the example below, the type of ``[1, 2, 3]`` is
``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``.
::
@ -878,13 +892,7 @@ assigned to a variable right away.
}
}
The type of an array literal is a memory array of fixed size whose base
type is the common type of the given elements. The type of ``[1, 2, 3]`` is
``uint8[3] memory``, because the type of each of these constants is ``uint8``.
Because of that, it is necessary to convert the first element in the example
above to ``uint``. Note that currently, fixed size memory arrays cannot
be assigned to dynamically-sized memory arrays, i.e. the following is not
possible:
Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible:
::
@ -899,8 +907,8 @@ possible:
}
}
It is planned to remove this restriction in the future but currently creates
some complications because of how arrays are passed in the ABI.
It is planned to remove this restriction in the future, but it creates some
complications because of how arrays are passed in the ABI.
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
@ -913,7 +921,9 @@ Members
For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array.
Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion.
Increasing the length adds new zero-initialised elements to the array.
Reducing the length performs an implicit :ref:``delete`` on each of the removed elements.
Reducing the length performs an implicit :ref:``delete`` on each of the
removed elements. If you try to resize a non-dynamic array that isn't in
storage, you receive a ``Value must be an lvalue`` error.
**push**:
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length.
**pop**:

View File

@ -43,8 +43,8 @@ library has to be updated by an external oracle.
.. note::
The suffix ``years`` has been removed in version 0.5.0 due to the reasons above.
These suffixes cannot be applied to variables. If you want to
interpret some input variable in e.g. days, you can do it in the following way::
These suffixes cannot be applied to variables. For example, if you want to
interpret a function parameter in days, you can in the following way::
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) {
@ -125,7 +125,7 @@ ABI Encoding and Decoding Functions
These encoding functions can be used to craft data for external function calls without actually
calling an external function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a way
to compute the hash of structured data (although be aware that it is possible to
craft a "hash collision" using different inputs types).
craft a "hash collision" using different function parameter types).
See the documentation about the :ref:`ABI <ABI>` and the
:ref:`tightly packed encoding <abi_packed_mode>` for details about the encoding.
@ -240,4 +240,3 @@ Furthermore, all functions of the current contract are callable directly includi
.. note::
Prior to version 0.5.0, there was a function called ``suicide`` with the same
semantics as ``selfdestruct``.

View File

@ -369,10 +369,10 @@ The following functions must be available:
+---------------------------------------------+-----------------------------------------------------------------+
| gtu256(x:u256, y:u256) -> z:bool | true if x > y, false otherwise |
+---------------------------------------------+-----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise |
| lts256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise |
| | (for signed numbers in two's complement) |
+---------------------------------------------+-----------------------------------------------------------------+
| sgtu256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise |
| gts256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise |
| | (for signed numbers in two's complement) |
+---------------------------------------------+-----------------------------------------------------------------+
| equ256(x:u256, y:u256) -> z:bool | true if x == y, false otherwise |
@ -391,7 +391,7 @@ The following functions must be available:
+---------------------------------------------+-----------------------------------------------------------------+
| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y |
+---------------------------------------------+-----------------------------------------------------------------+
| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y |
| sars256(x:s256, y:u256) -> z:u256 | arithmetic right shift of x by y |
+---------------------------------------------+-----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte |
| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and |
@ -515,6 +515,16 @@ The following functions must be available:
+---------------------------------------------+-----------------------------------------------------------------+
| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
+---------------------------------------------+-----------------------------------------------------------------+
| *Object access* | |
+---------------------------------------------+-----------------------------------------------------------------+
| datasize(name:string) -> size:u256 | size of the data object in bytes, name has to be string literal |
+---------------------------------------------+-----------------------------------------------------------------+
| dataoffset(name:string) -> offset:u256 | offset of the data object inside the data area in bytes, |
| | name has to be string literal |
+---------------------------------------------+-----------------------------------------------------------------+
| datacopy(dst:u256, src:u256, len:u256) | copy len bytes from the data area starting at offset src bytes |
| | to memory at position dst |
+---------------------------------------------+-----------------------------------------------------------------+
Backends
--------
@ -540,12 +550,18 @@ TBD
Specification of Yul Object
===========================
Yul objects are used to group named code and data sections.
The functions ``datasize``, ``dataoffset`` and ``datacopy``
can be used to access these sections from within code.
Hex strings can be used to specify data in hex encoding,
regular strings in native encoding. For code,
``datacopy`` will access its assembled binary representation.
Grammar::
TopLevelObject = 'object' '{' Code? ( Object | Data )* '}'
Object = 'object' StringLiteral '{' Code? ( Object | Data )* '}'
Object = 'object' StringLiteral '{' Code ( Object | Data )* '}'
Code = 'code' Block
Data = 'data' StringLiteral HexLiteral
Data = 'data' StringLiteral ( HexLiteral | StringLiteral )
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
@ -558,14 +574,28 @@ An example Yul Object is shown below:
// Code consists of a single object. A single "code" node is the code of the object.
// Every (other) named object or data section is serialized and
// made accessible to the special built-in functions datacopy / dataoffset / datasize
object {
// Access to nested objects can be performed by joining the names using ``.``.
// The current object and sub-objects and data items inside the current object
// are in scope without nested access.
object "Contract1" {
code {
let size = datasize("runtime")
// first create "runtime.Contract2"
let size = datasize("runtime.Contract2")
let offset = allocate(size)
// This will turn into a memory->memory copy for eWASM and
// a codecopy for EVM
datacopy(dataoffset("runtime"), offset, size)
// this is a constructor and the runtime code is returned
datacopy(offset, dataoffset("runtime.Contract2"), size)
// constructor parameter is a single number 0x1234
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))
// now return the runtime object (this is
// constructor code)
size := datasize("runtime")
offset := allocate(size)
// This will turn into a memory->memory copy for eWASM and
// a codecopy for EVM
datacopy(offset, dataoffset("runtime"), size)
return(offset, size)
}
@ -579,7 +609,7 @@ An example Yul Object is shown below:
let offset = allocate(size)
// This will turn into a memory->memory copy for eWASM and
// a codecopy for EVM
datacopy(dataoffset("Contract2"), offset, size)
datacopy(offset, dataoffset("Contract2"), size)
// constructor parameter is a single number 0x1234
mstore(add(offset, size), 0x1234)
create(offset, add(size, 32))

View File

@ -1,7 +1,17 @@
file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
set(sources
CommonData.cpp
CommonIO.cpp
Exceptions.cpp
IndentedWriter.cpp
JSON.cpp
Keccak256.cpp
StringUtils.cpp
SwarmHash.cpp
UTF8.cpp
Whiskers.cpp
)
add_library(devcore ${sources} ${headers})
add_library(devcore ${sources})
target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})

View File

@ -21,6 +21,8 @@
#include "JSON.h"
#include "CommonIO.h"
#include <sstream>
#include <map>
#include <memory>
@ -111,4 +113,10 @@ bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = null
return parse(readerBuilder, _input, _json, _errs);
}
bool jsonParseFile(string const& _fileName, Json::Value& _json, string *_errs /* = nullptr */)
{
return jsonParse(readFileAsString(_fileName), _json, _errs);
}
} // namespace dev

View File

@ -48,4 +48,11 @@ bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string*
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr);
/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json)
/// \param _input file containing JSON input
/// \param _json [out] resulting JSON object
/// \param _errs [out] Formatted error messages
/// \return \c true if the document was successfully parsed, \c false if an error occurred.
bool jsonParseFile(std::string const& _fileName, Json::Value& _json, std::string* _errs = nullptr);
}

View File

@ -35,6 +35,7 @@
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
void Assembly::append(Assembly const& _a)
{
@ -113,10 +114,10 @@ namespace
string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location)
{
if (_location.isEmpty() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0)
if (_location.isEmpty() || !_location.source.get() || _sourceCodes.empty() || _location.start >= _location.end || _location.start < 0)
return "";
auto it = _sourceCodes.find(*_location.sourceName);
auto it = _sourceCodes.find(_location.source->name());
if (it == _sourceCodes.end())
return "";
@ -185,11 +186,11 @@ public:
void printLocation()
{
if (!m_location.sourceName && m_location.isEmpty())
if (!m_location.source && m_location.isEmpty())
return;
m_out << m_prefix << " /*";
if (m_location.sourceName)
m_out << " \"" + *m_location.sourceName + "\"";
if (m_location.source)
m_out << " \"" + m_location.source->name() + "\"";
if (!m_location.isEmpty())
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
m_out << " " << locationFromSources(m_sourceCodes, m_location);

View File

@ -18,12 +18,12 @@
#pragma once
#include <libevmasm/Instruction.h>
#include <libevmasm/SourceLocation.h>
#include <liblangutil/SourceLocation.h>
#include <libevmasm/AssemblyItem.h>
#include <libevmasm/LinkerObject.h>
#include <libevmasm/Exceptions.h>
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
@ -93,7 +93,7 @@ public:
void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
/// Changes the source location used for each appended item.
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; }
/// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.
LinkerObject const& assemble() const;
@ -178,7 +178,7 @@ protected:
int m_deposit = 0;
SourceLocation m_currentSourceLocation;
langutil::SourceLocation m_currentSourceLocation;
};
inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)

View File

@ -117,6 +117,8 @@ int AssemblyItem::returnValues() const
bool AssemblyItem::canBeFunctional() const
{
if (m_jumpType != JumpType::Ordinary)
return false;
switch (m_type)
{
case Operation:

View File

@ -26,7 +26,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/SourceLocation.h>
#include <liblangutil/SourceLocation.h>
#include "Exceptions.h"
using namespace dev::solidity;
@ -57,14 +57,14 @@ class AssemblyItem
public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
AssemblyItem(u256 _push, SourceLocation const& _location = SourceLocation()):
AssemblyItem(u256 _push, langutil::SourceLocation const& _location = langutil::SourceLocation()):
AssemblyItem(Push, _push, _location) { }
AssemblyItem(solidity::Instruction _i, SourceLocation const& _location = SourceLocation()):
AssemblyItem(solidity::Instruction _i, langutil::SourceLocation const& _location = langutil::SourceLocation()):
m_type(Operation),
m_instruction(_i),
m_location(_location)
{}
AssemblyItem(AssemblyItemType _type, u256 _data = 0, SourceLocation const& _location = SourceLocation()):
AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation const& _location = langutil::SourceLocation()):
m_type(_type),
m_location(_location)
{
@ -124,8 +124,8 @@ public:
/// @returns true if the assembly item can be used in a functional context.
bool canBeFunctional() const;
void setLocation(SourceLocation const& _location) { m_location = _location; }
SourceLocation const& location() const { return m_location; }
void setLocation(langutil::SourceLocation const& _location) { m_location = _location; }
langutil::SourceLocation const& location() const { return m_location; }
void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
JumpType getJumpType() const { return m_jumpType; }
@ -140,7 +140,7 @@ private:
AssemblyItemType m_type;
Instruction m_instruction; ///< Only valid if m_type == Operation
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
SourceLocation m_location;
langutil::SourceLocation m_location;
JumpType m_jumpType = JumpType::Ordinary;
/// Pushed value for operations with data to be determined during assembly stage,
/// e.g. PushSubSize, PushTag, PushSub, etc.

View File

@ -1,5 +1,21 @@
file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
set(sources
Assembly.cpp
AssemblyItem.cpp
BlockDeduplicator.cpp
CommonSubexpressionEliminator.cpp
ConstantOptimiser.cpp
ControlFlowGraph.cpp
ExpressionClasses.cpp
GasMeter.cpp
Instruction.cpp
JumpdestRemover.cpp
KnownState.cpp
LinkerObject.cpp
PathGasMeter.cpp
PeepholeOptimiser.cpp
SemanticInformation.cpp
SimplificationRules.cpp
)
add_library(evmasm ${sources} ${headers})
add_library(evmasm ${sources})
target_link_libraries(evmasm PUBLIC devcore)

View File

@ -30,6 +30,7 @@
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{

View File

@ -34,6 +34,11 @@
#include <libevmasm/SemanticInformation.h>
#include <libevmasm/KnownState.h>
namespace langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
@ -137,10 +142,10 @@ private:
bool removeStackTopIfPossible();
/// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position.
void appendDup(int _fromPosition, SourceLocation const& _location);
void appendDup(int _fromPosition, langutil::SourceLocation const& _location);
/// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position.
/// @note this might also remove the last item if it exactly the same swap instruction.
void appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location);
void appendOrRemoveSwap(int _fromPosition, langutil::SourceLocation const& _location);
/// Appends the given assembly item.
void appendItem(AssemblyItem const& _item);

View File

@ -193,7 +193,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
bigint bestGas = gasNeeded(routine);
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)
continue;

View File

@ -23,7 +23,7 @@
#include <libevmasm/Exceptions.h>
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/CommonData.h>
@ -109,8 +109,8 @@ class LiteralMethod: public ConstantOptimisationMethod
public:
explicit LiteralMethod(Params const& _params, u256 const& _value):
ConstantOptimisationMethod(_params, _value) {}
virtual bigint gasNeeded() const override;
virtual AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; }
bigint gasNeeded() const override;
AssemblyItems execute(Assembly&) const override { return AssemblyItems{}; }
};
/**
@ -120,8 +120,8 @@ class CodeCopyMethod: public ConstantOptimisationMethod
{
public:
explicit CodeCopyMethod(Params const& _params, u256 const& _value);
virtual bigint gasNeeded() const override;
virtual AssemblyItems execute(Assembly& _assembly) const override;
bigint gasNeeded() const override;
AssemblyItems execute(Assembly& _assembly) const override;
protected:
static AssemblyItems const& copyRoutine();
@ -144,8 +144,8 @@ public:
);
}
virtual bigint gasNeeded() const override { return gasNeeded(m_routine); }
virtual AssemblyItems execute(Assembly&) const override
bigint gasNeeded() const override { return gasNeeded(m_routine); }
AssemblyItems execute(Assembly&) const override
{
return m_routine;
}

View File

@ -275,7 +275,7 @@ void ControlFlowGraph::gatherKnowledge()
//@todo in the case of JUMPI, add knowledge about the condition to the state
// (for both values of the condition)
set<u256> tags = state->tagsInExpression(
state->stackElement(state->stackHeight(), SourceLocation())
state->stackElement(state->stackHeight(), langutil::SourceLocation{})
);
state->feedItem(m_items.at(pc++));

View File

@ -34,7 +34,7 @@
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
{

View File

@ -31,6 +31,11 @@
#include <memory>
#include <set>
namespace langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
@ -82,7 +87,7 @@ public:
void forceEqual(Id _id, AssemblyItem const& _item, Ids const& _arguments, bool _copyItem = true);
/// @returns the id of a new class which is different to all other classes.
Id newClass(SourceLocation const& _location);
Id newClass(langutil::SourceLocation const& _location);
/// @returns true if the values of the given classes are known to be different (on every input).
/// @note that this function might still return false for some different inputs.

View File

@ -24,7 +24,7 @@
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/AssemblyItem.h>
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <ostream>
#include <tuple>

View File

@ -29,6 +29,7 @@
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
ostream& KnownState::stream(ostream& _out) const
{

View File

@ -46,6 +46,11 @@
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SemanticInformation.h>
namespace langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
@ -121,9 +126,9 @@ public:
/// Retrieves the current equivalence class fo the given stack element (or generates a new
/// one if it does not exist yet).
Id stackElement(int _stackHeight, SourceLocation const& _location);
Id stackElement(int _stackHeight, langutil::SourceLocation const& _location);
/// @returns the stackElement relative to the current stack height.
Id relativeStackElement(int _stackOffset, SourceLocation const& _location = SourceLocation());
Id relativeStackElement(int _stackOffset, langutil::SourceLocation const& _location = {});
/// @returns its set of tags if the given expression class is a known tag union; returns a set
/// containing the tag if it is a PushTag expression and the empty set otherwise.
@ -142,22 +147,22 @@ private:
/// Assigns a new equivalence class to the next sequence number of the given stack element.
void setStackElement(int _stackHeight, Id _class);
/// Swaps the given stack elements in their next sequence number.
void swapStackElements(int _stackHeightA, int _stackHeightB, SourceLocation const& _location);
void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::SourceLocation const& _location);
/// Increments the sequence number, deletes all storage information that might be overwritten
/// and stores the new value at the given slot.
/// @returns the store operation, which might be invalid if storage was not modified
StoreOperation storeInStorage(Id _slot, Id _value, SourceLocation const& _location);
StoreOperation storeInStorage(Id _slot, Id _value, langutil::SourceLocation const& _location);
/// Retrieves the current value at the given slot in storage or creates a new special sload class.
Id loadFromStorage(Id _slot, SourceLocation const& _location);
Id loadFromStorage(Id _slot, langutil::SourceLocation const& _location);
/// Increments the sequence number, deletes all memory information that might be overwritten
/// and stores the new value at the given slot.
/// @returns the store operation, which might be invalid if memory was not modified
StoreOperation storeInMemory(Id _slot, Id _value, SourceLocation const& _location);
StoreOperation storeInMemory(Id _slot, Id _value, langutil::SourceLocation const& _location);
/// Retrieves the current value at the given slot in memory or creates a new special mload class.
Id loadFromMemory(Id _slot, SourceLocation const& _location);
Id loadFromMemory(Id _slot, langutil::SourceLocation const& _location);
/// Finds or creates a new expression that applies the Keccak-256 hash function to the contents in memory.
Id applyKeccak256(Id _start, Id _length, SourceLocation const& _location);
Id applyKeccak256(Id _start, Id _length, langutil::SourceLocation const& _location);
/// @returns a new or already used Id representing the given set of tags.
Id tagUnion(std::set<u256> _tags);

View File

@ -23,7 +23,7 @@
#include <libevmasm/GasMeter.h>
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <set>
#include <vector>

View File

@ -38,7 +38,7 @@
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
SimplificationRule<Pattern> const* Rules::findFirstMatch(
Expression const& _expr,

View File

@ -31,6 +31,11 @@
#include <functional>
#include <vector>
namespace langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
@ -97,7 +102,7 @@ public:
unsigned matchGroup() const { return m_matchGroup; }
bool matches(Expression const& _expr, ExpressionClasses const& _classes) const;
AssemblyItem toAssemblyItem(SourceLocation const& _location) const;
AssemblyItem toAssemblyItem(langutil::SourceLocation const& _location) const;
std::vector<Pattern> arguments() const { return m_arguments; }
/// @returns the id of the matched expression if this pattern is part of a match group.
@ -135,7 +140,7 @@ struct ExpressionTemplate
{
using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id;
explicit ExpressionTemplate(Pattern const& _pattern, SourceLocation const& _location);
explicit ExpressionTemplate(Pattern const& _pattern, langutil::SourceLocation const& _location);
std::string toString() const;
bool hasId = false;
/// Id of the matched expression, if available.

View File

@ -0,0 +1,13 @@
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
set(sources
CharStream.cpp
ErrorReporter.cpp
Exceptions.cpp
ParserBase.cpp
Scanner.cpp
SourceReferenceFormatter.cpp
Token.cpp
)
add_library(langutil ${sources})
target_link_libraries(langutil PUBLIC devcore)

108
liblangutil/CharStream.cpp Normal file
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/>.
This file is derived from the file "scanner.cc", which was part of the
V8 project. The original copyright header follows:
Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
*/
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
using namespace std;
using namespace langutil;
char CharStream::advanceAndGet(size_t _chars)
{
if (isPastEndOfInput())
return 0;
m_position += _chars;
if (isPastEndOfInput())
return 0;
return m_source[m_position];
}
char CharStream::rollback(size_t _amount)
{
solAssert(m_position >= _amount, "");
m_position -= _amount;
return get();
}
string CharStream::lineAtPosition(int _position) const
{
// if _position points to \n, it returns the line before the \n
using size_type = string::size_type;
size_type searchStart = min<size_type>(m_source.size(), _position);
if (searchStart > 0)
searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == string::npos)
lineStart = 0;
else
lineStart++;
return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
m_source.size()) - lineStart);
}
tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{
using size_type = string::size_type;
size_type searchPosition = min<size_type>(m_source.size(), _position);
int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0)
lineStart = 0;
else
{
lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == string::npos ? 0 : lineStart + 1;
}
return tuple<int, int>(lineNumber, searchPosition - lineStart);
}

100
liblangutil/CharStream.h Normal file
View File

@ -0,0 +1,100 @@
/*
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/>.
This file is derived from the file "scanner.h", which was part of the
V8 project. The original copyright header follows:
Copyright 2006-2012, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
*/
#pragma once
#include <cstdint>
#include <string>
#include <tuple>
namespace langutil
{
/**
* Bidirectional stream of characters.
*
* This CharStream is used by lexical analyzers as the source.
*/
class CharStream
{
public:
CharStream(): m_position(0) {}
explicit CharStream(std::string const& _source, std::string const& name):
m_source(_source), m_name(name), m_position(0) {}
int position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; }
char advanceAndGet(size_t _chars = 1);
char rollback(size_t _amount);
void reset() { m_position = 0; }
std::string const& source() const noexcept { return m_source; }
std::string const& name() const noexcept { return m_name; }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
std::string lineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
///@}
private:
std::string m_source;
std::string m_name;
size_t m_position;
};
}

View File

@ -20,13 +20,13 @@
* Error helper class.
*/
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/SourceLocation.h>
#include <memory>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace langutil;
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
{

View File

@ -22,15 +22,11 @@
#pragma once
#include <libsolidity/interface/Exceptions.h>
#include <libevmasm/SourceLocation.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceLocation.h>
namespace dev
namespace langutil
{
namespace solidity
{
class ASTNode;
class ErrorReporter
{
@ -120,7 +116,5 @@ private:
const unsigned c_maxErrorsAllowed = 256;
};
}
}

View File

@ -20,11 +20,11 @@
* Solidity exception hierarchy.
*/
#include <libsolidity/interface/Exceptions.h>
#include <liblangutil/Exceptions.h>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace langutil;
Error::Error(Type _type, SourceLocation const& _location, string const& _description):
m_type(_type)

View File

@ -24,33 +24,33 @@
#include <string>
#include <utility>
#include <vector>
#include <memory>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Assertions.h>
#include <libevmasm/SourceLocation.h>
#include <liblangutil/SourceLocation.h>
namespace dev
{
namespace solidity
namespace langutil
{
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
struct FatalError: virtual Exception {};
struct UnimplementedFeatureError: virtual Exception{};
struct CompilerError: virtual dev::Exception {};
struct InternalCompilerError: virtual dev::Exception {};
struct FatalError: virtual dev::Exception {};
struct UnimplementedFeatureError: virtual dev::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION)
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION)
#define solUnimplemented(DESCRIPTION) \
solUnimplementedAssert(false, DESCRIPTION)
class Error: virtual public Exception
class Error: virtual public dev::Exception
{
public:
enum class Type
@ -98,7 +98,6 @@ private:
std::string m_typeName;
};
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
class SecondarySourceLocation
@ -109,6 +108,7 @@ public:
infos.push_back(std::make_pair(_errMsg, _sourceLocation));
return *this;
}
/// Limits the number of secondary source locations to 32 and appends a notice to the
/// error message.
void limitSize(std::string& _message)
@ -124,9 +124,8 @@ public:
std::vector<errorSourceLocationInfo> infos;
};
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>;
using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>;
}
}

View File

@ -20,18 +20,12 @@
* Solidity parser shared functionality.
*/
#include <libsolidity/parsing/ParserBase.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ParserBase.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace dev;
using namespace dev::solidity;
std::shared_ptr<string const> const& ParserBase::sourceName() const
{
return m_scanner->sourceName();
}
using namespace langutil;
int ParserBase::position() const
{
@ -106,10 +100,10 @@ void ParserBase::decreaseRecursionDepth()
void ParserBase::parserError(string const& _description)
{
m_errorReporter.parserError(SourceLocation(position(), endPosition(), sourceName()), _description);
m_errorReporter.parserError(SourceLocation(position(), endPosition(), source()), _description);
}
void ParserBase::fatalParserError(string const& _description)
{
m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), sourceName()), _description);
m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), source()), _description);
}

View File

@ -22,12 +22,12 @@
#pragma once
#include <liblangutil/Token.h>
#include <liblangutil/Scanner.h>
#include <memory>
#include <libsolidity/parsing/Token.h>
#include <string>
namespace dev
{
namespace solidity
namespace langutil
{
class ErrorReporter;
@ -38,7 +38,7 @@ class ParserBase
public:
explicit ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {}
std::shared_ptr<std::string const> const& sourceName() const;
std::shared_ptr<CharStream> source() const { return m_scanner->charStream(); }
protected:
/// Utility class that creates an error and throws an exception if the
@ -90,4 +90,3 @@ protected:
};
}
}

View File

@ -50,16 +50,15 @@
* Solidity scanner.
*/
#include <liblangutil/Exceptions.h>
#include <liblangutil/Scanner.h>
#include <algorithm>
#include <ostream>
#include <tuple>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/parsing/Scanner.h>
using namespace std;
namespace dev
{
namespace solidity
namespace langutil
{
namespace
@ -102,7 +101,32 @@ int hexValue(char c)
}
} // end anonymous namespace
std::string to_string(ScannerError _errorCode)
{
switch (_errorCode)
{
case ScannerError::NoError: return "No error.";
case ScannerError::IllegalToken: return "Invalid token.";
case ScannerError::IllegalHexString: return "Expected even number of hex-nibbles within double-quotes.";
case ScannerError::IllegalHexDigit: return "Hexadecimal digit missing or invalid.";
case ScannerError::IllegalCommentTerminator: return "Expected multi-line comment-terminator.";
case ScannerError::IllegalEscapeSequence: return "Invalid escape sequence.";
case ScannerError::IllegalStringEndQuote: return "Expected string end-quote.";
case ScannerError::IllegalNumberSeparator: return "Invalid use of number separator '_'.";
case ScannerError::IllegalExponent: return "Invalid exponent.";
case ScannerError::IllegalNumberEnd: return "Identifier-start is not allowed at end of a number.";
case ScannerError::OctalNotAllowed: return "Octal numbers not allowed.";
default:
solAssert(false, "Unhandled case in to_string(ScannerError)");
return "";
}
}
std::ostream& operator<<(std::ostream& os, ScannerError _errorCode)
{
os << to_string(_errorCode);
return os;
}
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
@ -143,17 +167,23 @@ private:
}; // end of LiteralScope class
void Scanner::reset(CharStream const& _source, string const& _sourceName)
void Scanner::reset(CharStream _source)
{
m_source = make_shared<CharStream>(std::move(_source));
reset();
}
void Scanner::reset(std::shared_ptr<CharStream> _source)
{
solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting.");
m_source = _source;
m_sourceName = make_shared<string const>(_sourceName);
reset();
}
void Scanner::reset()
{
m_source.reset();
m_char = m_source.get();
m_source->reset();
m_char = m_source->get();
skipWhitespace();
scanToken();
next();
@ -272,13 +302,13 @@ Token Scanner::scanSingleLineDocComment()
{
// check if next line is also a documentation comment
skipWhitespace();
if (!m_source.isPastEndOfInput(3) &&
m_source.get(0) == '/' &&
m_source.get(1) == '/' &&
m_source.get(2) == '/')
if (!m_source->isPastEndOfInput(3) &&
m_source->get(0) == '/' &&
m_source->get(1) == '/' &&
m_source->get(2) == '/')
{
addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(3);
m_char = m_source->advanceAndGet(3);
}
else
break; // next line is not a documentation comment, we are done
@ -313,7 +343,7 @@ Token Scanner::skipMultiLineComment()
}
}
// Unterminated multi-line comment.
return Token::Illegal;
return setError(ScannerError::IllegalCommentTerminator);
}
Token Scanner::scanMultiLineDocComment()
@ -331,20 +361,20 @@ Token Scanner::scanMultiLineDocComment()
if (isLineTerminator(m_char))
{
skipWhitespace();
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '*')
if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*')
{ // it is unknown if this leads to the end of the comment
addCommentLiteralChar('*');
advance();
}
else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/')
else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/')
{ // skip first '*' in subsequent lines
if (charsAdded)
addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(2);
m_char = m_source->advanceAndGet(2);
}
else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/')
{ // if after newline the comment ends, don't insert the newline
m_char = m_source.advanceAndGet(2);
m_char = m_source->advanceAndGet(2);
endFound = true;
break;
}
@ -352,9 +382,9 @@ Token Scanner::scanMultiLineDocComment()
addCommentLiteralChar('\n');
}
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/')
{
m_char = m_source.advanceAndGet(2);
m_char = m_source->advanceAndGet(2);
endFound = true;
break;
}
@ -364,7 +394,7 @@ Token Scanner::scanMultiLineDocComment()
}
literal.complete();
if (!endFound)
return Token::Illegal;
return setError(ScannerError::IllegalCommentTerminator);
else
return Token::CommentLiteral;
}
@ -394,7 +424,7 @@ Token Scanner::scanSlash()
{
// doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */
return Token::Illegal;
return setError(ScannerError::IllegalCommentTerminator);
else if (m_char == '*')
{
advance(); //consume the last '*' at /**
@ -412,7 +442,7 @@ Token Scanner::scanSlash()
m_nextSkippedComment.location.end = sourcePos();
m_nextSkippedComment.token = comment;
if (comment == Token::Illegal)
return Token::Illegal;
return Token::Illegal; // error already set
else
return Token::Whitespace;
}
@ -427,6 +457,7 @@ Token Scanner::scanSlash()
void Scanner::scanToken()
{
m_nextToken.error = ScannerError::NoError;
m_nextToken.literal.clear();
m_nextToken.extendedTokenInfo = make_tuple(0, 0);
m_nextSkippedComment.literal.clear();
@ -612,7 +643,7 @@ void Scanner::scanToken()
if (m_char == '"' || m_char == '\'')
token = scanHexString();
else
token = Token::IllegalHex;
token = setError(ScannerError::IllegalToken);
}
}
else if (isDecimalDigit(m_char))
@ -622,7 +653,7 @@ void Scanner::scanToken()
else if (isSourcePastEndOfInput())
token = Token::EOS;
else
token = selectToken(Token::Illegal);
token = selectErrorToken(ScannerError::IllegalToken);
break;
}
// Continue scanning for tokens as long as we're just skipping
@ -690,11 +721,11 @@ bool Scanner::isUnicodeLinebreak()
if (0x0a <= m_char && m_char <= 0x0d)
// line feed, vertical tab, form feed, carriage return
return true;
else if (!m_source.isPastEndOfInput(1) && uint8_t(m_source.get(0)) == 0xc2 && uint8_t(m_source.get(1)) == 0x85)
else if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85)
// NEL - U+0085, C2 85 in utf8
return true;
else if (!m_source.isPastEndOfInput(2) && uint8_t(m_source.get(0)) == 0xe2 && uint8_t(m_source.get(1)) == 0x80 && (
uint8_t(m_source.get(2)) == 0xa8 || uint8_t(m_source.get(2)) == 0xa9
else if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && (
uint8_t(m_source->get(2)) == 0xa8 || uint8_t(m_source->get(2)) == 0xa9
))
// LS - U+2028, E2 80 A8 in utf8
// PS - U+2029, E2 80 A9 in utf8
@ -715,13 +746,13 @@ Token Scanner::scanString()
if (c == '\\')
{
if (isSourcePastEndOfInput() || !scanEscape())
return Token::Illegal;
return setError(ScannerError::IllegalEscapeSequence);
}
else
addLiteralChar(c);
}
if (m_char != quote)
return Token::Illegal;
return setError(ScannerError::IllegalStringEndQuote);
literal.complete();
advance(); // consume quote
return Token::StringLiteral;
@ -736,11 +767,14 @@ Token Scanner::scanHexString()
{
char c = m_char;
if (!scanHexByte(c))
return Token::IllegalHex;
// can only return false if hex-byte is incomplete (only one hex digit instead of two)
return setError(ScannerError::IllegalHexString);
addLiteralChar(c);
}
if (m_char != quote)
return Token::IllegalHex;
return setError(ScannerError::IllegalStringEndQuote);
literal.complete();
advance(); // consume quote
return Token::StringLiteral;
@ -755,7 +789,7 @@ void Scanner::scanDecimalDigits()
// May continue with decimal digit or underscore for grouping.
do addLiteralCharAndAdvance();
while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_'));
while (!m_source->isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_'));
// Defer further validation of underscore to SyntaxChecker.
}
@ -769,7 +803,7 @@ Token Scanner::scanNumber(char _charSeen)
// we have already seen a decimal point of the float
addLiteralChar('.');
if (m_char == '_')
return Token::Illegal;
return setError(ScannerError::IllegalToken);
scanDecimalDigits(); // we know we have at least one digit
}
else
@ -786,14 +820,14 @@ Token Scanner::scanNumber(char _charSeen)
kind = HEX;
addLiteralCharAndAdvance();
if (!isHexDigit(m_char))
return Token::Illegal; // we must have at least one hex digit after 'x'
return setError(ScannerError::IllegalHexDigit); // we must have at least one hex digit after 'x'
while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation
addLiteralCharAndAdvance();
}
else if (isDecimalDigit(m_char))
// We do not allow octal numbers
return Token::Illegal;
return setError(ScannerError::OctalNotAllowed);
}
// Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL)
@ -801,7 +835,7 @@ Token Scanner::scanNumber(char _charSeen)
scanDecimalDigits(); // optional
if (m_char == '.')
{
if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_')
if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_')
{
// Assume the input may be a floating point number with leading '_' in fraction part.
// Recover by consuming it all but returning `Illegal` right away.
@ -809,7 +843,7 @@ Token Scanner::scanNumber(char _charSeen)
addLiteralCharAndAdvance(); // '_'
scanDecimalDigits();
}
if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1)))
if (m_source->isPastEndOfInput() || !isDecimalDigit(m_source->get(1)))
{
// A '.' has to be followed by a number.
literal.complete();
@ -825,8 +859,8 @@ Token Scanner::scanNumber(char _charSeen)
{
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
if (kind != DECIMAL)
return Token::Illegal;
else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_')
return setError(ScannerError::IllegalExponent);
else if (!m_source->isPastEndOfInput(1) && m_source->get(1) == '_')
{
// Recover from wrongly placed underscore as delimiter in literal with scientific
// notation by consuming until the end.
@ -840,8 +874,8 @@ Token Scanner::scanNumber(char _charSeen)
addLiteralCharAndAdvance(); // 'e' | 'E'
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!isDecimalDigit(m_char))
return Token::Illegal; // we must have at least one decimal digit after 'e'/'E'
if (!isDecimalDigit(m_char)) // we must have at least one decimal digit after 'e'/'E'
return setError(ScannerError::IllegalExponent);
scanDecimalDigits();
}
// The source character immediately following a numeric literal must
@ -849,7 +883,7 @@ Token Scanner::scanNumber(char _charSeen)
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
if (isDecimalDigit(m_char) || isIdentifierStart(m_char))
return Token::Illegal;
return setError(ScannerError::IllegalNumberEnd);
literal.complete();
return Token::Number;
}
@ -866,55 +900,5 @@ tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal);
}
char CharStream::advanceAndGet(size_t _chars)
{
if (isPastEndOfInput())
return 0;
m_position += _chars;
if (isPastEndOfInput())
return 0;
return m_source[m_position];
}
char CharStream::rollback(size_t _amount)
{
solAssert(m_position >= _amount, "");
m_position -= _amount;
return get();
}
string CharStream::lineAtPosition(int _position) const
{
// if _position points to \n, it returns the line before the \n
using size_type = string::size_type;
size_type searchStart = min<size_type>(m_source.size(), _position);
if (searchStart > 0)
searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == string::npos)
lineStart = 0;
else
lineStart++;
return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
m_source.size()) - lineStart);
}
tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{
using size_type = string::size_type;
size_type searchPosition = min<size_type>(m_source.size(), _position);
int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0)
lineStart = 0;
else
{
lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == string::npos ? 0 : lineStart + 1;
}
return tuple<int, int>(lineNumber, searchPosition - lineStart);
}
}
}

View File

@ -52,62 +52,54 @@
#pragma once
#include <liblangutil/Token.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/SourceLocation.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libevmasm/SourceLocation.h>
#include <libsolidity/parsing/Token.h>
#include <iosfwd>
namespace dev
namespace langutil
{
namespace solidity
{
class AstRawString;
class AstValueFactory;
class ParserRecorder;
class CharStream
enum class ScannerError
{
public:
CharStream(): m_position(0) {}
explicit CharStream(std::string const& _source): m_source(_source), m_position(0) {}
int position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; }
char advanceAndGet(size_t _chars = 1);
char rollback(size_t _amount);
NoError,
void reset() { m_position = 0; }
IllegalToken,
IllegalHexString,
IllegalHexDigit,
IllegalCommentTerminator,
IllegalEscapeSequence,
IllegalStringEndQuote,
IllegalNumberSeparator,
IllegalExponent,
IllegalNumberEnd,
std::string const& source() const { return m_source; }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
std::string lineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
///@}
private:
std::string m_source;
size_t m_position;
OctalNotAllowed,
};
std::string to_string(ScannerError _errorCode);
std::ostream& operator<<(std::ostream& os, ScannerError _errorCode);
class Scanner
{
friend class LiteralScope;
public:
explicit Scanner(std::shared_ptr<CharStream> _source) { reset(std::move(_source)); }
explicit Scanner(CharStream _source = CharStream()) { reset(std::move(_source)); }
explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); }
std::string const& source() const noexcept { return m_source->source(); }
std::string source() const { return m_source.source(); }
std::shared_ptr<CharStream> charStream() noexcept { return m_source; }
/// Resets the scanner as if newly constructed with _source and _sourceName as input.
void reset(CharStream const& _source, std::string const& _sourceName);
/// Resets the scanner as if newly constructed with _source as input.
void reset(CharStream _source);
void reset(std::shared_ptr<CharStream> _source);
/// Resets scanner to the start of input.
void reset();
@ -133,6 +125,10 @@ public:
SourceLocation currentLocation() const { return m_currentToken.location; }
std::string const& currentLiteral() const { return m_currentToken.literal; }
std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_currentToken.extendedTokenInfo; }
/// Retrieves the last error that occurred during lexical analysis.
/// @note If no error occurred, the value is undefined.
ScannerError currentError() const noexcept { return m_currentToken.error; }
///@}
///@{
@ -154,30 +150,34 @@ public:
std::string const& peekLiteral() const { return m_nextToken.literal; }
///@}
std::shared_ptr<std::string const> const& sourceName() const { return m_sourceName; }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
std::string lineAtPosition(int _position) const { return m_source.lineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); }
std::string sourceAt(SourceLocation const& _location) const
{
solAssert(!_location.isEmpty(), "");
solAssert(m_sourceName && _location.sourceName, "");
solAssert(*m_sourceName == *_location.sourceName, "");
return m_source.source().substr(_location.start, _location.end - _location.start);
solAssert(m_source.get() == _location.source.get(), "CharStream memory locations must match.");
return m_source->source().substr(_location.start, _location.end - _location.start);
}
///@}
private:
inline Token setError(ScannerError _error) noexcept
{
m_nextToken.error = _error;
return Token::Illegal;
}
/// Used for the current and look-ahead token and comments
struct TokenDesc
{
Token token;
SourceLocation location;
std::string literal;
ScannerError error = ScannerError::NoError;
std::tuple<unsigned, unsigned> extendedTokenInfo;
};
@ -189,9 +189,10 @@ private:
void addUnicodeAsUTF8(unsigned codepoint);
///@}
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source.rollback(_amount); }
bool advance() { m_char = m_source->advanceAndGet(); return !m_source->isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source->rollback(_amount); }
inline Token selectErrorToken(ScannerError _err) { advance(); return setError(_err); }
inline Token selectToken(Token _tok) { advance(); return _tok; }
/// If the next character is _next, advance and return _then, otherwise return _else.
inline Token selectToken(char _next, Token _then, Token _else);
@ -229,8 +230,8 @@ private:
bool isUnicodeLinebreak();
/// Return the current source position.
int sourcePos() const { return m_source.position(); }
bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); }
int sourcePos() const { return m_source->position(); }
bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); }
TokenDesc m_skippedComment; // desc for current skipped comment
TokenDesc m_nextSkippedComment; // desc for next skipped comment
@ -238,12 +239,10 @@ private:
TokenDesc m_currentToken; // desc for current token (as returned by Next())
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
CharStream m_source;
std::shared_ptr<std::string const> m_sourceName;
std::shared_ptr<CharStream> m_source;
/// one character look-ahead, equals 0 at end of input
char m_char;
};
}
}

View File

@ -22,13 +22,14 @@
#pragma once
#include <libdevcore/Common.h> // defines noexcept macro for MSVC
#include <liblangutil/CharStream.h>
#include <memory>
#include <string>
#include <ostream>
#include <tuple>
#include <libdevcore/Common.h> // defines noexcept macro for MSVC
namespace dev
namespace langutil
{
/**
@ -37,28 +38,13 @@ namespace dev
*/
struct SourceLocation
{
SourceLocation(): start(-1), end(-1) { }
SourceLocation(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
start(_start), end(_end), sourceName(_sourceName) { }
SourceLocation(SourceLocation&& _other) noexcept:
start(_other.start),
end(_other.end),
sourceName(std::move(_other.sourceName))
{}
SourceLocation(SourceLocation const&) = default;
SourceLocation& operator=(SourceLocation const&) = default;
SourceLocation& operator=(SourceLocation&& _other) noexcept
{
start = _other.start;
end = _other.end;
sourceName = std::move(_other.sourceName);
return *this;
}
SourceLocation(): start(-1), end(-1), source{nullptr} { }
SourceLocation(int _start, int _end, std::shared_ptr<CharStream> _source):
start(_start), end(_end), source{std::move(_source)} { }
bool operator==(SourceLocation const& _other) const
{
return start == _other.start && end == _other.end &&
((!sourceName && !_other.sourceName) || (sourceName && _other.sourceName && *sourceName == *_other.sourceName));
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
}
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
inline bool operator<(SourceLocation const& _other) const;
@ -69,7 +55,7 @@ struct SourceLocation
int start;
int end;
std::shared_ptr<std::string const> sourceName;
std::shared_ptr<CharStream> source;
};
/// Stream output for Location (used e.g. in boost exceptions).
@ -77,27 +63,33 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
{
if (_location.isEmpty())
return _out << "NO_LOCATION_SPECIFIED";
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
if (_location.source)
_out << _location.source->name();
_out << "[" << _location.start << "," << _location.end << ")";
return _out;
}
bool SourceLocation::operator<(SourceLocation const& _other) const
{
if (!sourceName || !_other.sourceName)
return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end);
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
else
return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end);
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
}
bool SourceLocation::contains(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName)))
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return start <= _other.start && _other.end <= end;
}
bool SourceLocation::intersects(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName)))
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return _other.start < end && start < _other.end;
}

View File

@ -20,22 +20,19 @@
* Formatting functions for errors referencing positions and locations in the source.
*/
#include <libsolidity/interface/SourceReferenceFormatter.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/Exceptions.h>
using namespace std;
namespace dev
{
namespace solidity
{
using namespace dev;
using namespace langutil;
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
{
if (!_location || !_location->sourceName)
if (!_location || !_location->source)
return; // Nothing we can print here
auto const& scanner = m_scannerFromSourceName(*_location->sourceName);
auto const& scanner = m_scannerFromSourceName(_location->source->name());
int startLine;
int startColumn;
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
@ -92,17 +89,17 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
{
if (!_location || !_location->sourceName)
if (!_location || !_location->source)
return; // Nothing we can print here
auto const& scanner = m_scannerFromSourceName(*_location->sourceName);
auto const& scanner = m_scannerFromSourceName(_location->source->name());
int startLine;
int startColumn;
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
m_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
m_stream << _location->source->name() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
}
void SourceReferenceFormatter::printExceptionInformation(
Exception const& _exception,
dev::Exception const& _exception,
string const& _name
)
{
@ -130,6 +127,3 @@ void SourceReferenceFormatter::printExceptionInformation(
m_stream << endl;
}
}
}
}

View File

@ -25,23 +25,21 @@
#include <ostream>
#include <sstream>
#include <functional>
#include <libevmasm/SourceLocation.h>
namespace dev
{
struct Exception; // forward
}
namespace solidity
namespace langutil
{
class Scanner; // forward
class CompilerStack; // forward
struct SourceLocation;
class Scanner;
class SourceReferenceFormatter
{
public:
using ScannerFromSourceNameFun = std::function<Scanner const&(std::string const&)>;
using ScannerFromSourceNameFun = std::function<langutil::Scanner const&(std::string const&)>;
explicit SourceReferenceFormatter(
std::ostream& _stream,
@ -52,11 +50,11 @@ public:
{}
/// Prints source location if it is given.
void printSourceLocation(SourceLocation const* _location);
void printExceptionInformation(Exception const& _exception, std::string const& _name);
void printSourceLocation(langutil::SourceLocation const* _location);
void printExceptionInformation(dev::Exception const& _exception, std::string const& _name);
static std::string formatExceptionInformation(
Exception const& _exception,
dev::Exception const& _exception,
std::string const& _name,
ScannerFromSourceNameFun const& _scannerFromSourceName
)
@ -69,11 +67,10 @@ public:
}
private:
/// Prints source name if location is given.
void printSourceName(SourceLocation const* _location);
void printSourceName(langutil::SourceLocation const* _location);
std::ostream& m_stream;
ScannerFromSourceNameFun m_scannerFromSourceName;
};
}
}

View File

@ -40,15 +40,13 @@
// You should have received a copy of the GNU General Public License
// along with solidity. If not, see <http://www.gnu.org/licenses/>.
#include <map>
#include <libsolidity/parsing/Token.h>
#include <liblangutil/Token.h>
#include <boost/range/iterator_range.hpp>
#include <map>
using namespace std;
namespace dev
{
namespace solidity
namespace langutil
{
void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second)
@ -204,4 +202,3 @@ tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _
}
}
}

376
liblangutil/Token.h Normal file
View File

@ -0,0 +1,376 @@
// Copyright 2006-2012, the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Modifications as part of solidity under the following license:
//
// 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 <libdevcore/Common.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/UndefMacros.h>
#include <iosfwd>
#include <string>
#include <tuple>
namespace langutil
{
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
// same signature M(name, string, precedence), where name is the
// symbolic token name, string is the corresponding syntactic symbol
// (or nullptr, for literals), and precedence is the precedence (or 0).
// The parameters are invoked for token categories as follows:
//
// T: Non-keyword tokens
// K: Keyword tokens
// IGNORE_TOKEN is a convenience macro that can be supplied as
// an argument (at any position) for a TOKEN_LIST call. It does
// nothing with tokens belonging to the respective category.
#define IGNORE_TOKEN(name, string, precedence)
#define TOKEN_LIST(T, K) \
/* End of source indicator. */ \
T(EOS, "EOS", 0) \
\
/* Punctuators (ECMA-262, section 7.7, page 15). */ \
T(LParen, "(", 0) \
T(RParen, ")", 0) \
T(LBrack, "[", 0) \
T(RBrack, "]", 0) \
T(LBrace, "{", 0) \
T(RBrace, "}", 0) \
T(Colon, ":", 0) \
T(Semicolon, ";", 0) \
T(Period, ".", 0) \
T(Conditional, "?", 3) \
T(Arrow, "=>", 0) \
\
/* Assignment operators. */ \
/* IsAssignmentOp() relies on this block of enum values being */ \
/* contiguous and sorted in the same order!*/ \
T(Assign, "=", 2) \
/* The following have to be in exactly the same order as the simple binary operators*/ \
T(AssignBitOr, "|=", 2) \
T(AssignBitXor, "^=", 2) \
T(AssignBitAnd, "&=", 2) \
T(AssignShl, "<<=", 2) \
T(AssignSar, ">>=", 2) \
T(AssignShr, ">>>=", 2) \
T(AssignAdd, "+=", 2) \
T(AssignSub, "-=", 2) \
T(AssignMul, "*=", 2) \
T(AssignDiv, "/=", 2) \
T(AssignMod, "%=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(Comma, ",", 1) \
T(Or, "||", 4) \
T(And, "&&", 5) \
T(BitOr, "|", 8) \
T(BitXor, "^", 9) \
T(BitAnd, "&", 10) \
T(SHL, "<<", 11) \
T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \
T(Add, "+", 12) \
T(Sub, "-", 12) \
T(Mul, "*", 13) \
T(Div, "/", 13) \
T(Mod, "%", 13) \
T(Exp, "**", 14) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(Equal, "==", 6) \
T(NotEqual, "!=", 6) \
T(LessThan, "<", 7) \
T(GreaterThan, ">", 7) \
T(LessThanOrEqual, "<=", 7) \
T(GreaterThanOrEqual, ">=", 7) \
\
/* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(Not, "!", 0) \
T(BitNot, "~", 0) \
T(Inc, "++", 0) \
T(Dec, "--", 0) \
K(Delete, "delete", 0) \
\
/* Keywords */ \
K(Anonymous, "anonymous", 0) \
K(As, "as", 0) \
K(Assembly, "assembly", 0) \
K(Break, "break", 0) \
K(Constant, "constant", 0) \
K(Constructor, "constructor", 0) \
K(Continue, "continue", 0) \
K(Contract, "contract", 0) \
K(Do, "do", 0) \
K(Else, "else", 0) \
K(Enum, "enum", 0) \
K(Emit, "emit", 0) \
K(Event, "event", 0) \
K(External, "external", 0) \
K(For, "for", 0) \
K(Function, "function", 0) \
K(Hex, "hex", 0) \
K(If, "if", 0) \
K(Indexed, "indexed", 0) \
K(Interface, "interface", 0) \
K(Internal, "internal", 0) \
K(Import, "import", 0) \
K(Is, "is", 0) \
K(Library, "library", 0) \
K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
K(New, "new", 0) \
K(Payable, "payable", 0) \
K(Public, "public", 0) \
K(Pragma, "pragma", 0) \
K(Private, "private", 0) \
K(Pure, "pure", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(CallData, "calldata", 0) \
K(Struct, "struct", 0) \
K(Throw, "throw", 0) \
K(Using, "using", 0) \
K(Var, "var", 0) \
K(View, "view", 0) \
K(While, "while", 0) \
\
/* Ether subdenominations */ \
K(SubWei, "wei", 0) \
K(SubSzabo, "szabo", 0) \
K(SubFinney, "finney", 0) \
K(SubEther, "ether", 0) \
K(SubSecond, "seconds", 0) \
K(SubMinute, "minutes", 0) \
K(SubHour, "hours", 0) \
K(SubDay, "days", 0) \
K(SubWeek, "weeks", 0) \
K(SubYear, "years", 0) \
/* type keywords*/ \
K(Int, "int", 0) \
K(UInt, "uint", 0) \
K(Bytes, "bytes", 0) \
K(Byte, "byte", 0) \
K(String, "string", 0) \
K(Address, "address", 0) \
K(Bool, "bool", 0) \
K(Fixed, "fixed", 0) \
K(UFixed, "ufixed", 0) \
T(IntM, "intM", 0) \
T(UIntM, "uintM", 0) \
T(BytesM, "bytesM", 0) \
T(FixedMxN, "fixedMxN", 0) \
T(UFixedMxN, "ufixedMxN", 0) \
T(TypesEnd, nullptr, 0) /* used as type enum end marker */ \
\
/* Literals */ \
K(TrueLiteral, "true", 0) \
K(FalseLiteral, "false", 0) \
T(Number, nullptr, 0) \
T(StringLiteral, nullptr, 0) \
T(CommentLiteral, nullptr, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
T(Identifier, nullptr, 0) \
\
/* Keywords reserved for future use. */ \
K(Abstract, "abstract", 0) \
K(After, "after", 0) \
K(Alias, "alias", 0) \
K(Apply, "apply", 0) \
K(Auto, "auto", 0) \
K(Case, "case", 0) \
K(Catch, "catch", 0) \
K(CopyOf, "copyof", 0) \
K(Default, "default", 0) \
K(Define, "define", 0) \
K(Final, "final", 0) \
K(Immutable, "immutable", 0) \
K(Implements, "implements", 0) \
K(In, "in", 0) \
K(Inline, "inline", 0) \
K(Let, "let", 0) \
K(Macro, "macro", 0) \
K(Match, "match", 0) \
K(Mutable, "mutable", 0) \
K(NullLiteral, "null", 0) \
K(Of, "of", 0) \
K(Override, "override", 0) \
K(Partial, "partial", 0) \
K(Promise, "promise", 0) \
K(Reference, "reference", 0) \
K(Relocatable, "relocatable", 0) \
K(Sealed, "sealed", 0) \
K(Sizeof, "sizeof", 0) \
K(Static, "static", 0) \
K(Supports, "supports", 0) \
K(Switch, "switch", 0) \
K(Try, "try", 0) \
K(Type, "type", 0) \
K(Typedef, "typedef", 0) \
K(TypeOf, "typeof", 0) \
K(Unchecked, "unchecked", 0) \
\
/* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
T(Whitespace, nullptr, 0)
// All token values.
// attention! msvc issue:
// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059
// @todo: avoid TOKEN_LIST macro
enum class Token : unsigned int {
#define T(name, string, precedence) name,
TOKEN_LIST(T, T)
NUM_TOKENS
#undef T
};
namespace TokenTraits
{
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
// Predicates
constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; }
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; }
constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; }
constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; }
constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; }
constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; }
constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; }
constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; }
constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); }
constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; }
constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; }
constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; }
constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true)
{
return (op == Token::Constant && _allowConstant)
|| op == Token::Pure || op == Token::View || op == Token::Payable;
}
constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; }
constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; }
constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); }
inline Token AssignmentToBinaryOp(Token op)
{
solAssert(isAssignmentOp(op) && op != Token::Assign, "");
return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr)));
}
// @returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
int precedence(Token tok);
std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal);
// @returns a string corresponding to the C++ token name
// (e.g. "LT" for the token LT).
char const* name(Token tok);
// @returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or nullptr if the token doesn't
// have a (unique) string (e.g. an IDENTIFIER).
char const* toString(Token tok);
std::string friendlyName(Token tok);
}
inline std::ostream& operator<<(std::ostream& os, Token token)
{
os << TokenTraits::friendlyName(token);
return os;
}
class ElementaryTypeNameToken
{
public:
ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber)
{
assertDetails(_token, _firstNumber, _secondNumber);
}
unsigned int firstNumber() const { return m_firstNumber; }
unsigned int secondNumber() const { return m_secondNumber; }
Token token() const { return m_token; }
///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type
std::string toString(bool const& tokenValue = false) const
{
std::string name = TokenTraits::toString(m_token);
if (tokenValue || (firstNumber() == 0 && secondNumber() == 0))
return name;
solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here.");
if (m_token == Token::FixedMxN || m_token == Token::UFixedMxN)
return name.substr(0, name.size() - 3) + std::to_string(m_firstNumber) + "x" + std::to_string(m_secondNumber);
else
return name.substr(0, name.size() - 1) + std::to_string(m_firstNumber);
}
private:
Token m_token;
unsigned int m_firstNumber;
unsigned int m_secondNumber;
/// throws if type is not properly sized
void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second);
};
}

View File

@ -1,5 +1,9 @@
file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
set(sources
CodeFragment.cpp
Compiler.cpp
CompilerState.cpp
Parser.cpp
)
add_library(lll ${sources} ${headers})
add_library(lll ${sources})
target_link_libraries(lll PUBLIC evmasm devcore)

View File

@ -259,6 +259,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
}
else if (us == "SET")
{
// TODO: move this to be a stack variable (and not a memory variable)
if (_t.size() != 3)
error<IncorrectParameterCount>(us);
int c = 0;
@ -268,6 +269,15 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
m_asm.append((u256)varAddress(firstAsString(), true));
m_asm.append(Instruction::MSTORE);
}
else if (us == "UNSET")
{
// TODO: this doesn't actually free up anything, since it is a memory variable (see "SET")
if (_t.size() != 2)
error<IncorrectParameterCount>();
auto it = _s.vars.find(firstAsString());
if (it != _s.vars.end())
_s.vars.erase(it);
}
else if (us == "GET")
{
if (_t.size() != 2)
@ -275,6 +285,35 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
m_asm.append((u256)varAddress(firstAsString()));
m_asm.append(Instruction::MLOAD);
}
else if (us == "WITH")
{
if (_t.size() != 4)
error<IncorrectParameterCount>();
string key = firstAsString();
if (_s.vars.find(key) != _s.vars.end())
error<InvalidName>(string("Symbol already used: ") + key);
// Create variable
// TODO: move this to be a stack variable (and not a memory variable)
size_t c = 0;
for (auto const& i: _t)
if (c++ == 2)
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
m_asm.append((u256)varAddress(key, true));
m_asm.append(Instruction::MSTORE);
// Insert sub with variable access, but new state
CompilerState ns = _s;
c = 0;
for (auto const& i: _t)
if (c++ == 3)
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
// Remove variable
auto it = _s.vars.find(key);
if (it != _s.vars.end())
_s.vars.erase(it);
}
else if (us == "REF")
m_asm.append((u256)varAddress(firstAsString()));
else if (us == "DEF")

View File

@ -23,7 +23,7 @@
#include <libdevcore/Common.h>
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <string>
#include <vector>

View File

@ -1,14 +1,60 @@
# Until we have a clear separation, libyul has to be included here
file(GLOB_RECURSE sources "*.cpp" "../libyul/*.cpp")
file(GLOB_RECURSE headers "*.h" "../libyul/*.h")
set(sources
analysis/ConstantEvaluator.cpp
analysis/ContractLevelChecker.cpp
analysis/ControlFlowAnalyzer.cpp
analysis/ControlFlowBuilder.cpp
analysis/ControlFlowGraph.cpp
analysis/DeclarationContainer.cpp
analysis/DocStringAnalyser.cpp
analysis/GlobalContext.cpp
analysis/NameAndTypeResolver.cpp
analysis/PostTypeChecker.cpp
analysis/ReferencesResolver.cpp
analysis/SemVerHandler.cpp
analysis/StaticAnalyzer.cpp
analysis/SyntaxChecker.cpp
analysis/TypeChecker.cpp
analysis/ViewPureChecker.cpp
ast/AST.cpp
ast/ASTAnnotations.cpp
ast/ASTJsonConverter.cpp
ast/ASTPrinter.cpp
ast/Types.cpp
codegen/ABIFunctions.cpp
codegen/ArrayUtils.cpp
codegen/Compiler.cpp
codegen/CompilerContext.cpp
codegen/CompilerUtils.cpp
codegen/ContractCompiler.cpp
codegen/ExpressionCompiler.cpp
codegen/LValue.cpp
formal/SMTChecker.cpp
formal/SMTLib2Interface.cpp
formal/SMTPortfolio.cpp
formal/SSAVariable.cpp
formal/SymbolicTypes.cpp
formal/SymbolicVariables.cpp
formal/VariableUsage.cpp
interface/ABI.cpp
interface/AssemblyStack.cpp
interface/CompilerStack.cpp
interface/GasEstimator.cpp
interface/Natspec.cpp
interface/StandardCompiler.cpp
interface/Version.cpp
parsing/DocStringParser.cpp
parsing/Parser.cpp
)
find_package(Z3 QUIET)
if (${Z3_FOUND})
include_directories(${Z3_INCLUDE_DIR})
add_definitions(-DHAVE_Z3)
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
set(z3_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
else()
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
set(z3_SRCS)
endif()
find_package(CVC4 QUIET)
@ -16,8 +62,9 @@ if (${CVC4_FOUND})
include_directories(${CVC4_INCLUDE_DIR})
add_definitions(-DHAVE_CVC4)
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
set(cvc4_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
else()
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
set(cvc4_SRCS)
endif()
if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
@ -25,8 +72,8 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
\nPlease install Z3 or CVC4 or remove the option disabling them (USE_Z3, USE_CVC4).")
endif()
add_library(solidity ${sources} ${headers})
target_link_libraries(solidity PUBLIC evmasm devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})

View File

@ -22,7 +22,7 @@
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace dev;

View File

@ -24,12 +24,16 @@
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
class TypeChecker;
/**
@ -39,7 +43,7 @@ class ConstantEvaluator: private ASTConstVisitor
{
public:
ConstantEvaluator(
ErrorReporter& _errorReporter,
langutil::ErrorReporter& _errorReporter,
size_t _newDepth = 0,
std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>()
):
@ -61,7 +65,7 @@ private:
void setType(ASTNode const& _node, TypePointer const& _type);
TypePointer type(ASTNode const& _node);
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
/// Current recursion depth.
size_t m_depth = 0;
std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types;

View File

@ -0,0 +1,463 @@
/*
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/>.
*/
/**
* Component that verifies overloads, abstract contracts, function clashes and others
* checks at contract or function level.
*/
#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
bool ContractLevelChecker::check(ContractDefinition const& _contract)
{
checkDuplicateFunctions(_contract);
checkDuplicateEvents(_contract);
checkIllegalOverrides(_contract);
checkAbstractFunctions(_contract);
checkBaseConstructorArguments(_contract);
checkConstructor(_contract);
checkFallbackFunction(_contract);
checkExternalTypeClashes(_contract);
checkHashCollisions(_contract);
checkLibraryRequirements(_contract);
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract)
{
/// Checks that two functions with the same name defined in this contract have different
/// argument types and that there is at most one constructor.
map<string, vector<FunctionDefinition const*>> functions;
FunctionDefinition const* constructor = nullptr;
FunctionDefinition const* fallback = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
if (function->isConstructor())
{
if (constructor)
m_errorReporter.declarationError(
function->location(),
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
"More than one constructor defined."
);
constructor = function;
}
else if (function->isFallback())
{
if (fallback)
m_errorReporter.declarationError(
function->location(),
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
"Only one fallback function is allowed."
);
fallback = function;
}
else
{
solAssert(!function->name().empty(), "");
functions[function->name()].push_back(function);
}
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
}
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
{
/// Checks that two events with the same name defined in this contract have different
/// argument types
map<string, vector<EventDefinition const*>> events;
for (EventDefinition const* event: _contract.events())
events[event->name()].push_back(event);
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
}
template <class T>
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
{
for (auto const& it: _definitions)
{
vector<T> const& overloads = it.second;
set<size_t> reported;
for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
{
SecondarySourceLocation ssl;
for (size_t j = i + 1; j < overloads.size(); ++j)
if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes(
*FunctionType(*overloads[j]).asCallableFunction(false))
)
{
ssl.append("Other declaration is here:", overloads[j]->location());
reported.insert(j);
}
if (ssl.infos.size() > 0)
{
ssl.limitSize(_message);
m_errorReporter.declarationError(
overloads[i]->location(),
ssl,
_message
);
}
}
}
}
void ContractLevelChecker::checkIllegalOverrides(ContractDefinition const& _contract)
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
// into the types
map<string, vector<FunctionDefinition const*>> functions;
map<string, ModifierDefinition const*> modifiers;
// We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{
for (FunctionDefinition const* function: contract->definedFunctions())
{
if (function->isConstructor())
continue; // constructors can neither be overridden nor override anything
string const& name = function->name();
if (modifiers.count(name))
m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
for (FunctionDefinition const* overriding: functions[name])
checkFunctionOverride(*overriding, *function);
functions[name].push_back(function);
}
for (ModifierDefinition const* modifier: contract->functionModifiers())
{
string const& name = modifier->name();
ModifierDefinition const*& override = modifiers[name];
if (!override)
override = modifier;
else if (ModifierType(*override) != ModifierType(*modifier))
m_errorReporter.typeError(override->location(), "Override changes modifier signature.");
if (!functions[name].empty())
m_errorReporter.typeError(override->location(), "Override changes modifier to function.");
}
}
}
void ContractLevelChecker::checkFunctionOverride(FunctionDefinition const& _function, FunctionDefinition const& _super)
{
FunctionTypePointer functionType = FunctionType(_function).asCallableFunction(false);
FunctionTypePointer superType = FunctionType(_super).asCallableFunction(false);
if (!functionType->hasEqualParameterTypes(*superType))
return;
if (!functionType->hasEqualReturnTypes(*superType))
overrideError(_function, _super, "Overriding function return types differ.");
if (!_function.annotation().superFunction)
_function.annotation().superFunction = &_super;
if (_function.visibility() != _super.visibility())
{
// Visibility change from external to public is fine.
// Any other change is disallowed.
if (!(
_super.visibility() == FunctionDefinition::Visibility::External &&
_function.visibility() == FunctionDefinition::Visibility::Public
))
overrideError(_function, _super, "Overriding function visibility differs.");
}
if (_function.stateMutability() != _super.stateMutability())
overrideError(
_function,
_super,
"Overriding function changes state mutability from \"" +
stateMutabilityToString(_super.stateMutability()) +
"\" to \"" +
stateMutabilityToString(_function.stateMutability()) +
"\"."
);
}
void ContractLevelChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
{
m_errorReporter.typeError(
function.location(),
SecondarySourceLocation().append("Overridden function is here:", super.location()),
message
);
}
void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _contract)
{
// Mapping from name to function definition (exactly one per argument type equality class) and
// flag to indicate whether it is fully implemented.
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions;
auto registerFunction = [&](Declaration const& _declaration, FunctionTypePointer const& _type, bool _implemented)
{
auto& overloads = functions[_declaration.name()];
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
{
return _type->hasEqualParameterTypes(*_funAndFlag.first);
});
if (it == overloads.end())
overloads.push_back(make_pair(_type, _implemented));
else if (it->second)
{
if (!_implemented)
m_errorReporter.typeError(_declaration.location(), "Redeclaring an already implemented function as abstract");
}
else if (_implemented)
it->second = true;
};
// Search from base to derived, collect all functions and update
// the 'implemented' flag.
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
{
for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface())
registerFunction(*v, make_shared<FunctionType>(*v), true);
for (FunctionDefinition const* function: contract->definedFunctions())
if (!function->isConstructor())
registerFunction(
*function,
make_shared<FunctionType>(*function)->asCallableFunction(false),
function->isImplemented()
);
}
// Set to not fully implemented if at least one flag is false.
for (auto const& it: functions)
for (auto const& funAndFlag: it.second)
if (!funAndFlag.second)
{
FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
solAssert(function, "");
_contract.annotation().unimplementedFunctions.push_back(function);
break;
}
}
void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract)
{
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
// Determine the arguments that are used for the base constructors.
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->constructor())
for (auto const& modifier: constructor->modifiers())
if (auto baseContract = dynamic_cast<ContractDefinition const*>(
modifier->name()->annotation().referencedDeclaration
))
{
if (modifier->arguments())
{
if (baseContract->constructor())
annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
}
else
m_errorReporter.declarationError(
modifier->location(),
"Modifier-style base constructor call without arguments."
);
}
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->name().annotation().referencedDeclaration
);
solAssert(baseContract, "");
if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
}
}
// check that we get arguments for all base constructors that need it.
// If not mark the contract as abstract (not fully implemented)
for (ContractDefinition const* contract: bases)
if (FunctionDefinition const* constructor = contract->constructor())
if (contract != &_contract && !constructor->parameters().empty())
if (!_contract.annotation().baseConstructorArguments.count(constructor))
_contract.annotation().unimplementedFunctions.push_back(constructor);
}
void ContractLevelChecker::annotateBaseConstructorArguments(
ContractDefinition const& _currentContract,
FunctionDefinition const* _baseConstructor,
ASTNode const* _argumentNode
)
{
solAssert(_baseConstructor, "");
solAssert(_argumentNode, "");
auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
std::make_pair(_baseConstructor, _argumentNode)
);
if (!insertionResult.second)
{
ASTNode const* previousNode = insertionResult.first->second;
SourceLocation const* mainLocation = nullptr;
SecondarySourceLocation ssl;
if (
_currentContract.location().contains(previousNode->location()) ||
_currentContract.location().contains(_argumentNode->location())
)
{
mainLocation = &previousNode->location();
ssl.append("Second constructor call is here:", _argumentNode->location());
}
else
{
mainLocation = &_currentContract.location();
ssl.append("First constructor call is here: ", _argumentNode->location());
ssl.append("Second constructor call is here: ", previousNode->location());
}
m_errorReporter.declarationError(
*mainLocation,
ssl,
"Base constructor arguments given twice."
);
}
}
void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract)
{
FunctionDefinition const* constructor = _contract.constructor();
if (!constructor)
return;
if (!constructor->returnParameters().empty())
m_errorReporter.typeError(constructor->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (constructor->stateMutability() != StateMutability::NonPayable && constructor->stateMutability() != StateMutability::Payable)
m_errorReporter.typeError(
constructor->location(),
"Constructor must be payable or non-payable, but is \"" +
stateMutabilityToString(constructor->stateMutability()) +
"\"."
);
if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal)
m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal.");
}
void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _contract)
{
FunctionDefinition const* fallback = _contract.fallbackFunction();
if (!fallback)
return;
if (_contract.isLibrary())
m_errorReporter.typeError(fallback->location(), "Libraries cannot have fallback functions.");
if (fallback->stateMutability() != StateMutability::NonPayable && fallback->stateMutability() != StateMutability::Payable)
m_errorReporter.typeError(
fallback->location(),
"Fallback function must be payable or non-payable, but is \"" +
stateMutabilityToString(fallback->stateMutability()) +
"\"."
);
if (!fallback->parameters().empty())
m_errorReporter.typeError(fallback->parameterList().location(), "Fallback function cannot take parameters.");
if (!fallback->returnParameters().empty())
m_errorReporter.typeError(fallback->returnParameterList()->location(), "Fallback function cannot return values.");
if (fallback->visibility() != FunctionDefinition::Visibility::External)
m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\".");
}
void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract)
{
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*f);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
externalDeclarations[functionType->externalSignature()].push_back(
make_pair(f, functionType->asCallableFunction(false))
);
}
for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*v);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
externalDeclarations[functionType->externalSignature()].push_back(
make_pair(v, functionType->asCallableFunction(false))
);
}
}
for (auto const& it: externalDeclarations)
for (size_t i = 0; i < it.second.size(); ++i)
for (size_t j = i + 1; j < it.second.size(); ++j)
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
m_errorReporter.typeError(
it.second[j].first->location(),
"Function overload clash during conversion to external types for arguments."
);
}
void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract)
{
set<FixedHash<4>> hashes;
for (auto const& it: _contract.interfaceFunctionList())
{
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
m_errorReporter.typeError(
_contract.location(),
string("Function signature hash collision for ") + it.second->externalSignature()
);
hashes.insert(hash);
}
}
void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract)
{
if (!_contract.isLibrary())
return;
if (!_contract.baseContracts().empty())
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
for (auto const& var: _contract.stateVariables())
if (!var->isConstant())
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
}

View File

@ -0,0 +1,87 @@
/*
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/>.
*/
/**
* Component that verifies overloads, abstract contracts, function clashes and others
* checks at contract or function level.
*/
#pragma once
#include <libsolidity/ast/ASTForward.h>
#include <map>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
{
/**
* Component that verifies overloads, abstract contracts, function clashes and others
* checks at contract or function level.
*/
class ContractLevelChecker
{
public:
/// @param _errorReporter provides the error logging functionality.
explicit ContractLevelChecker(langutil::ErrorReporter& _errorReporter):
m_errorReporter(_errorReporter)
{}
/// Performs checks on the given contract.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool check(ContractDefinition const& _contract);
private:
/// Checks that two functions defined in this contract with the same name have different
/// arguments and that there is at most one constructor.
void checkDuplicateFunctions(ContractDefinition const& _contract);
void checkDuplicateEvents(ContractDefinition const& _contract);
template <class T>
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
void checkIllegalOverrides(ContractDefinition const& _contract);
/// Reports a type error with an appropriate message if overridden function signature differs.
/// Also stores the direct super function in the AST annotations.
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
void checkAbstractFunctions(ContractDefinition const& _contract);
void checkBaseConstructorArguments(ContractDefinition const& _contract);
void annotateBaseConstructorArguments(
ContractDefinition const& _currentContract,
FunctionDefinition const* _baseConstructor,
ASTNode const* _argumentNode
);
void checkConstructor(ContractDefinition const& _contract);
void checkFallbackFunction(ContractDefinition const& _contract);
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkExternalTypeClashes(ContractDefinition const& _contract);
/// Checks for hash collisions in external function signatures.
void checkHashCollisions(ContractDefinition const& _contract);
/// Checks that all requirements for a library are fulfilled if this is a library.
void checkLibraryRequirements(ContractDefinition const& _contract);
langutil::ErrorReporter& m_errorReporter;
};
}
}

View File

@ -16,8 +16,10 @@
*/
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <liblangutil/SourceLocation.h>
using namespace std;
using namespace langutil;
using namespace dev::solidity;
bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot)

View File

@ -29,12 +29,12 @@ namespace solidity
class ControlFlowAnalyzer: private ASTConstVisitor
{
public:
explicit ControlFlowAnalyzer(CFG const& _cfg, ErrorReporter& _errorReporter):
explicit ControlFlowAnalyzer(CFG const& _cfg, langutil::ErrorReporter& _errorReporter):
m_cfg(_cfg), m_errorReporter(_errorReporter) {}
bool analyze(ASTNode const& _astRoot);
virtual bool visit(FunctionDefinition const& _function) override;
bool visit(FunctionDefinition const& _function) override;
private:
static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
@ -45,7 +45,7 @@ private:
) const;
CFG const& m_cfg;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
};
}

View File

@ -46,19 +46,19 @@ public:
private:
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
virtual bool visit(BinaryOperation const& _operation) override;
virtual bool visit(Conditional const& _conditional) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(ForStatement const& _forStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(Break const&) override;
virtual bool visit(Continue const&) override;
virtual bool visit(Throw const&) override;
virtual bool visit(Block const&) override;
virtual void endVisit(Block const&) override;
virtual bool visit(Return const& _return) override;
virtual bool visit(PlaceholderStatement const&) override;
virtual bool visit(FunctionCall const& _functionCall) override;
bool visit(BinaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(ForStatement const& _forStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
bool visit(Break const&) override;
bool visit(Continue const&) override;
bool visit(Throw const&) override;
bool visit(Block const&) override;
void endVisit(Block const&) override;
bool visit(Return const& _return) override;
bool visit(PlaceholderStatement const&) override;
bool visit(FunctionCall const& _functionCall) override;
/// Appends the control flow of @a _node to the current control flow.
@ -74,7 +74,7 @@ private:
protected:
virtual bool visitNode(ASTNode const& node) override;
bool visitNode(ASTNode const& node) override;
private:

View File

@ -23,6 +23,7 @@
#include <algorithm>
using namespace std;
using namespace langutil;
using namespace dev::solidity;
bool CFG::constructFlow(ASTNode const& _astRoot)
@ -133,4 +134,4 @@ void CFG::applyModifierFlowToFunctionFlow(
_functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
_functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
}
}

View File

@ -19,7 +19,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <map>
#include <memory>
@ -101,12 +101,12 @@ struct ModifierFlow: FunctionFlow
class CFG: private ASTConstVisitor
{
public:
explicit CFG(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
explicit CFG(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool constructFlow(ASTNode const& _astRoot);
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual bool visit(FunctionDefinition const& _function) override;
bool visit(ModifierDefinition const& _modifier) override;
bool visit(FunctionDefinition const& _function) override;
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
@ -133,7 +133,7 @@ private:
FunctionFlow* _functionFlow
);
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
/// Node container.
/// All nodes allocated during the construction of the control flow graph

View File

@ -23,11 +23,12 @@
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <libsolidity/parsing/DocStringParser.h>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)

View File

@ -25,13 +25,16 @@
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
/**
* Parses and analyses the doc strings.
* Stores the parsing results in the AST annotations and reports errors.
@ -39,14 +42,14 @@ class ErrorReporter;
class DocStringAnalyser: private ASTConstVisitor
{
public:
DocStringAnalyser(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
DocStringAnalyser(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool analyseDocStrings(SourceUnit const& _sourceUnit);
private:
virtual bool visit(ContractDefinition const& _contract) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual bool visit(EventDefinition const& _event) override;
bool visit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(ModifierDefinition const& _modifier) override;
bool visit(EventDefinition const& _event) override;
void checkParameters(
CallableDeclaration const& _callable,
@ -75,7 +78,7 @@ private:
void appendError(std::string const& _description);
bool m_errorOccured = false;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
};
}

View File

@ -24,12 +24,13 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace langutil;
namespace dev
{
@ -59,7 +60,7 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode
{
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope);
}
catch (FatalError const&)
catch (langutil::FatalError const&)
{
if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
@ -129,7 +130,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
{
return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
}
catch (FatalError const&)
catch (langutil::FatalError const&)
{
if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
@ -144,7 +145,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true);
solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
}
catch (FatalError const&)
catch (langutil::FatalError const&)
{
if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
@ -491,9 +492,9 @@ bool DeclarationRegistrationHelper::registerDeclaration(
Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name);
solAssert(conflictingDeclaration, "");
bool const comparable =
_errorLocation->sourceName &&
conflictingDeclaration->location().sourceName &&
*_errorLocation->sourceName == *conflictingDeclaration->location().sourceName;
_errorLocation->source &&
conflictingDeclaration->location().source &&
_errorLocation->source->name() == conflictingDeclaration->location().source->name();
if (comparable && _errorLocation->start < conflictingDeclaration->location().start)
{
firstDeclarationLocation = *_errorLocation;

View File

@ -30,13 +30,16 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
/**
* Resolves name references, typenames and sets the (explicitly given) types for all variable
* declarations.
@ -50,7 +53,7 @@ public:
NameAndTypeResolver(
std::vector<Declaration const*> const& _globals,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ErrorReporter& _errorReporter
langutil::ErrorReporter& _errorReporter
);
/// Registers all declarations found in the AST node, usually a source unit.
/// @returns false in case of error.
@ -125,7 +128,7 @@ private:
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
DeclarationContainer* m_currentScope = nullptr;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
};
/**
@ -142,7 +145,7 @@ public:
DeclarationRegistrationHelper(
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
ErrorReporter& _errorReporter,
langutil::ErrorReporter& _errorReporter,
ASTNode const* _currentScope = nullptr
);
@ -150,10 +153,10 @@ public:
DeclarationContainer& _container,
Declaration const& _declaration,
std::string const* _name,
SourceLocation const* _errorLocation,
langutil::SourceLocation const* _errorLocation,
bool _warnOnShadow,
bool _inactive,
ErrorReporter& _errorReporter
langutil::ErrorReporter& _errorReporter
);
private:
@ -194,7 +197,7 @@ private:
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
ASTNode const* m_currentScope = nullptr;
VariableScope* m_currentFunction = nullptr;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
};
}

View File

@ -18,7 +18,7 @@
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
#include <libdevcore/Algorithms.h>
@ -29,6 +29,7 @@
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;

View File

@ -23,13 +23,17 @@
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
struct SourceLocation;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
/**
* 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
@ -39,25 +43,25 @@ class PostTypeChecker: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool check(ASTNode const& _astRoot);
private:
/// Adds a new error to the list of errors.
void typeError(SourceLocation const& _location, std::string const& _description);
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
virtual bool visit(ContractDefinition const& _contract) override;
virtual void endVisit(ContractDefinition const& _contract) override;
bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
virtual bool visit(VariableDeclaration const& _variable) override;
virtual void endVisit(VariableDeclaration const& _variable) override;
bool visit(VariableDeclaration const& _variable) override;
void endVisit(VariableDeclaration const& _variable) override;
virtual bool visit(Identifier const& _identifier) override;
bool visit(Identifier const& _identifier) override;
VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom);
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
VariableDeclaration const* m_currentConstVariable = nullptr;
std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism.

View File

@ -23,12 +23,12 @@
#include <libsolidity/analysis/ReferencesResolver.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
#include <libdevcore/StringUtils.h>
@ -36,9 +36,12 @@
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace langutil;
namespace dev
{
namespace solidity
{
bool ReferencesResolver::resolve(ASTNode const& _root)
{
@ -270,7 +273,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
ErrorList errors;
ErrorReporter errorsIgnored(errors);
yul::ExternalIdentifierAccess::Resolver resolver =
[&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) {
[&](yul::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) {
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset");
@ -311,9 +314,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
// We use the latest EVM version because we will re-run it anyway.
assembly::AsmAnalysisInfo analysisInfo;
yul::AsmAnalysisInfo analysisInfo;
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
return false;
}
@ -454,3 +457,6 @@ void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location,
m_errorOccurred = true;
m_errorReporter.fatalDeclarationError(_location, _description);
}
}
}

View File

@ -28,12 +28,17 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
namespace langutil
{
class ErrorReporter;
struct SourceLocation;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
class NameAndTypeResolver;
/**
@ -44,7 +49,7 @@ class ReferencesResolver: private ASTConstVisitor
{
public:
ReferencesResolver(
ErrorReporter& _errorReporter,
langutil::ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver,
bool _resolveInsideCode = false
):
@ -57,38 +62,38 @@ public:
bool resolve(ASTNode const& _root);
private:
virtual bool visit(Block const& _block) override;
virtual void endVisit(Block const& _block) override;
virtual bool visit(ForStatement const& _for) override;
virtual void endVisit(ForStatement const& _for) override;
virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
virtual bool visit(Identifier const& _identifier) 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(FunctionTypeName const& _typeName) override;
virtual void endVisit(Mapping const& _typeName) override;
virtual void endVisit(ArrayTypeName const& _typeName) override;
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
virtual bool visit(Return const& _return) override;
virtual void endVisit(VariableDeclaration const& _variable) override;
bool visit(Block const& _block) override;
void endVisit(Block const& _block) override;
bool visit(ForStatement const& _for) override;
void endVisit(ForStatement const& _for) override;
void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
bool visit(Identifier const& _identifier) override;
bool visit(ElementaryTypeName const& _typeName) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const& _functionDefinition) override;
bool visit(ModifierDefinition const& _modifierDefinition) override;
void endVisit(ModifierDefinition const& _modifierDefinition) override;
void endVisit(UserDefinedTypeName const& _typeName) override;
void endVisit(FunctionTypeName const& _typeName) override;
void endVisit(Mapping const& _typeName) override;
void endVisit(ArrayTypeName const& _typeName) override;
bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(Return const& _return) override;
void endVisit(VariableDeclaration const& _variable) override;
/// Adds a new error to the list of errors.
void typeError(SourceLocation const& _location, std::string const& _description);
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors.
void declarationError(SourceLocation const& _location, std::string const& _description);
void declarationError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
/// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;

View File

@ -22,8 +22,9 @@
#pragma once
#include <vector>
#include <libsolidity/parsing/Token.h>
#include <string>
#include <vector>
namespace dev
{

View File

@ -23,11 +23,12 @@
#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <memory>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)

View File

@ -28,6 +28,11 @@
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
@ -44,7 +49,7 @@ class StaticAnalyzer: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
/// Performs static analysis on the given source unit and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
@ -52,25 +57,25 @@ public:
private:
virtual bool visit(ContractDefinition const& _contract) override;
virtual void endVisit(ContractDefinition const& _contract) override;
bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual void endVisit(FunctionDefinition const& _function) override;
bool visit(FunctionDefinition const& _function) override;
void endVisit(FunctionDefinition const& _function) override;
virtual bool visit(ExpressionStatement const& _statement) override;
virtual bool visit(VariableDeclaration const& _variable) override;
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(Return const& _return) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
virtual bool visit(BinaryOperation const& _operation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
bool visit(ExpressionStatement const& _statement) override;
bool visit(VariableDeclaration const& _variable) override;
bool visit(Identifier const& _identifier) override;
bool visit(Return const& _return) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(BinaryOperation const& _operation) override;
bool visit(FunctionCall const& _functionCall) override;
/// @returns the size of this type in storage, including all sub-types.
static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen);
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
/// Flag that indicates whether the current contract definition is a library.
bool m_library = false;

View File

@ -20,7 +20,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <liblangutil/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
#include <boost/algorithm/cxx11/all_of.hpp>
@ -29,6 +29,7 @@
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;

View File

@ -23,6 +23,11 @@
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
@ -39,49 +44,49 @@ class SyntaxChecker: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
SyntaxChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool checkSyntax(ASTNode const& _astRoot);
private:
virtual bool visit(SourceUnit const& _sourceUnit) override;
virtual void endVisit(SourceUnit const& _sourceUnit) override;
virtual bool visit(PragmaDirective const& _pragma) override;
bool visit(SourceUnit const& _sourceUnit) override;
void endVisit(SourceUnit const& _sourceUnit) override;
bool visit(PragmaDirective const& _pragma) override;
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual void endVisit(ModifierDefinition const& _modifier) override;
bool visit(ModifierDefinition const& _modifier) override;
void endVisit(ModifierDefinition const& _modifier) override;
/// Reports an error if _statement is a VariableDeclarationStatement.
/// Used by if/while/for to check for single statement variable declarations
/// without a block.
void checkSingleStatementVariableDeclaration(ASTNode const& _statement);
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual void endVisit(WhileStatement const& _whileStatement) override;
virtual bool visit(ForStatement const& _forStatement) override;
virtual void endVisit(ForStatement const& _forStatement) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
void endVisit(WhileStatement const& _whileStatement) override;
bool visit(ForStatement const& _forStatement) override;
void endVisit(ForStatement const& _forStatement) override;
virtual bool visit(Continue const& _continueStatement) override;
virtual bool visit(Break const& _breakStatement) override;
bool visit(Continue const& _continueStatement) override;
bool visit(Break const& _breakStatement) override;
virtual bool visit(Throw const& _throwStatement) override;
bool visit(Throw const& _throwStatement) override;
virtual bool visit(UnaryOperation const& _operation) override;
bool visit(UnaryOperation const& _operation) override;
virtual bool visit(PlaceholderStatement const& _placeholderStatement) override;
bool visit(PlaceholderStatement const& _placeholderStatement) override;
virtual bool visit(ContractDefinition const& _contract) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(FunctionTypeName const& _node) override;
bool visit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(FunctionTypeName const& _node) override;
virtual bool visit(VariableDeclarationStatement const& _statement) override;
bool visit(VariableDeclarationStatement const& _statement) override;
virtual bool visit(StructDefinition const& _struct) override;
virtual bool visit(Literal const& _literal) override;
bool visit(StructDefinition const& _struct) override;
bool visit(Literal const& _literal) override;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
/// Flag that indicates whether a function modifier actually contains '_'.
bool m_placeholderFound = false;

View File

@ -21,20 +21,27 @@
*/
#include <libsolidity/analysis/TypeChecker.h>
#include <memory>
#include <libsolidity/ast/AST.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/Algorithms.h>
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libdevcore/Algorithms.h>
#include <memory>
#include <vector>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
namespace
@ -81,418 +88,14 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
{
m_scope = &_contract;
// We force our own visiting order here. The structs have to be excluded below.
set<ASTNode const*> visited;
for (auto const& s: _contract.definedStructs())
visited.insert(s);
ASTNode::listAccept(_contract.definedStructs(), *this);
ASTNode::listAccept(_contract.baseContracts(), *this);
checkContractDuplicateFunctions(_contract);
checkContractDuplicateEvents(_contract);
checkContractIllegalOverrides(_contract);
checkContractAbstractFunctions(_contract);
checkContractBaseConstructorArguments(_contract);
FunctionDefinition const* function = _contract.constructor();
if (function)
{
if (!function->returnParameters().empty())
m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable)
m_errorReporter.typeError(
function->location(),
"Constructor must be payable or non-payable, but is \"" +
stateMutabilityToString(function->stateMutability()) +
"\"."
);
if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal)
m_errorReporter.typeError(function->location(), "Constructor must be public or internal.");
}
for (FunctionDefinition const* function: _contract.definedFunctions())
if (function->isFallback())
{
if (_contract.isLibrary())
m_errorReporter.typeError(function->location(), "Libraries cannot have fallback functions.");
if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable)
m_errorReporter.typeError(
function->location(),
"Fallback function must be payable or non-payable, but is \"" +
stateMutabilityToString(function->stateMutability()) +
"\"."
);
if (!function->parameters().empty())
m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters.");
if (!function->returnParameters().empty())
m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values.");
if (function->visibility() != FunctionDefinition::Visibility::External)
m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\".");
}
for (auto const& n: _contract.subNodes())
if (!visited.count(n.get()))
n->accept(*this);
checkContractExternalTypeClashes(_contract);
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& it: _contract.interfaceFunctionList())
{
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
m_errorReporter.typeError(
_contract.location(),
string("Function signature hash collision for ") + it.second->externalSignature()
);
hashes.insert(hash);
}
if (_contract.isLibrary())
checkLibraryRequirements(_contract);
n->accept(*this);
return false;
}
void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
{
/// Checks that two functions with the same name defined in this contract have different
/// argument types and that there is at most one constructor.
map<string, vector<FunctionDefinition const*>> functions;
FunctionDefinition const* constructor = nullptr;
FunctionDefinition const* fallback = nullptr;
for (FunctionDefinition const* function: _contract.definedFunctions())
if (function->isConstructor())
{
if (constructor)
m_errorReporter.declarationError(
function->location(),
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
"More than one constructor defined."
);
constructor = function;
}
else if (function->isFallback())
{
if (fallback)
m_errorReporter.declarationError(
function->location(),
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
"Only one fallback function is allowed."
);
fallback = function;
}
else
{
solAssert(!function->name().empty(), "");
functions[function->name()].push_back(function);
}
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
}
void TypeChecker::checkContractDuplicateEvents(ContractDefinition const& _contract)
{
/// Checks that two events with the same name defined in this contract have different
/// argument types
map<string, vector<EventDefinition const*>> events;
for (EventDefinition const* event: _contract.events())
events[event->name()].push_back(event);
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
}
template <class T>
void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
{
for (auto const& it: _definitions)
{
vector<T> const& overloads = it.second;
set<size_t> reported;
for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
{
SecondarySourceLocation ssl;
for (size_t j = i + 1; j < overloads.size(); ++j)
if (FunctionType(*overloads[i]).hasEqualParameterTypes(FunctionType(*overloads[j])))
{
ssl.append("Other declaration is here:", overloads[j]->location());
reported.insert(j);
}
if (ssl.infos.size() > 0)
{
ssl.limitSize(_message);
m_errorReporter.declarationError(
overloads[i]->location(),
ssl,
_message
);
}
}
}
}
void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract)
{
// Mapping from name to function definition (exactly one per argument type equality class) and
// flag to indicate whether it is fully implemented.
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
map<string, vector<FunTypeAndFlag>> functions;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
for (FunctionDefinition const* function: contract->definedFunctions())
{
// Take constructors out of overload hierarchy
if (function->isConstructor())
continue;
auto& overloads = functions[function->name()];
FunctionTypePointer funType = make_shared<FunctionType>(*function);
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
{
return funType->hasEqualParameterTypes(*_funAndFlag.first);
});
if (it == overloads.end())
overloads.push_back(make_pair(funType, function->isImplemented()));
else if (it->second)
{
if (!function->isImplemented())
m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract");
}
else if (function->isImplemented())
it->second = true;
}
// Set to not fully implemented if at least one flag is false.
for (auto const& it: functions)
for (auto const& funAndFlag: it.second)
if (!funAndFlag.second)
{
FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
solAssert(function, "");
_contract.annotation().unimplementedFunctions.push_back(function);
break;
}
}
void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract)
{
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
// Determine the arguments that are used for the base constructors.
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->constructor())
for (auto const& modifier: constructor->modifiers())
if (auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name())))
{
if (modifier->arguments())
{
if (baseContract->constructor())
annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
}
else
m_errorReporter.declarationError(
modifier->location(),
"Modifier-style base constructor call without arguments."
);
}
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
solAssert(baseContract, "");
if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
}
}
// check that we get arguments for all base constructors that need it.
// If not mark the contract as abstract (not fully implemented)
for (ContractDefinition const* contract: bases)
if (FunctionDefinition const* constructor = contract->constructor())
if (contract != &_contract && !constructor->parameters().empty())
if (!_contract.annotation().baseConstructorArguments.count(constructor))
_contract.annotation().unimplementedFunctions.push_back(constructor);
}
void TypeChecker::annotateBaseConstructorArguments(
ContractDefinition const& _currentContract,
FunctionDefinition const* _baseConstructor,
ASTNode const* _argumentNode
)
{
solAssert(_baseConstructor, "");
solAssert(_argumentNode, "");
auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
std::make_pair(_baseConstructor, _argumentNode)
);
if (!insertionResult.second)
{
ASTNode const* previousNode = insertionResult.first->second;
SourceLocation const* mainLocation = nullptr;
SecondarySourceLocation ssl;
if (
_currentContract.location().contains(previousNode->location()) ||
_currentContract.location().contains(_argumentNode->location())
)
{
mainLocation = &previousNode->location();
ssl.append("Second constructor call is here:", _argumentNode->location());
}
else
{
mainLocation = &_currentContract.location();
ssl.append("First constructor call is here: ", _argumentNode->location());
ssl.append("Second constructor call is here: ", previousNode->location());
}
m_errorReporter.declarationError(
*mainLocation,
ssl,
"Base constructor arguments given twice."
);
}
}
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
// into the types
map<string, vector<FunctionDefinition const*>> functions;
map<string, ModifierDefinition const*> modifiers;
// We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{
for (FunctionDefinition const* function: contract->definedFunctions())
{
if (function->isConstructor())
continue; // constructors can neither be overridden nor override anything
string const& name = function->name();
if (modifiers.count(name))
m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
for (FunctionDefinition const* overriding: functions[name])
checkFunctionOverride(*overriding, *function);
functions[name].push_back(function);
}
for (ModifierDefinition const* modifier: contract->functionModifiers())
{
string const& name = modifier->name();
ModifierDefinition const*& override = modifiers[name];
if (!override)
override = modifier;
else if (ModifierType(*override) != ModifierType(*modifier))
m_errorReporter.typeError(override->location(), "Override changes modifier signature.");
if (!functions[name].empty())
m_errorReporter.typeError(override->location(), "Override changes modifier to function.");
}
}
}
void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super)
{
FunctionType functionType(function);
FunctionType superType(super);
if (!functionType.hasEqualParameterTypes(superType))
return;
if (!function.annotation().superFunction)
function.annotation().superFunction = &super;
if (function.visibility() != super.visibility())
{
// visibility is enforced to be external in interfaces, but a contract can override that with public
if (
super.inContractKind() == ContractDefinition::ContractKind::Interface &&
function.inContractKind() != ContractDefinition::ContractKind::Interface &&
function.visibility() == FunctionDefinition::Visibility::Public
)
return;
overrideError(function, super, "Overriding function visibility differs.");
}
else if (function.stateMutability() != super.stateMutability())
overrideError(
function,
super,
"Overriding function changes state mutability from \"" +
stateMutabilityToString(super.stateMutability()) +
"\" to \"" +
stateMutabilityToString(function.stateMutability()) +
"\"."
);
else if (functionType != superType)
overrideError(function, super, "Overriding function return types differ.");
}
void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
{
m_errorReporter.typeError(
function.location(),
SecondarySourceLocation().append("Overridden function is here:", super.location()),
message
);
}
void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
{
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
{
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*f);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
externalDeclarations[functionType->externalSignature()].push_back(
make_pair(f, functionType)
);
}
for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*v);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
externalDeclarations[functionType->externalSignature()].push_back(
make_pair(v, functionType)
);
}
}
for (auto const& it: externalDeclarations)
for (size_t i = 0; i < it.second.size(); ++i)
for (size_t j = i + 1; j < it.second.size(); ++j)
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
m_errorReporter.typeError(
it.second[j].first->location(),
"Function overload clash during conversion to external types for arguments."
);
}
void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
{
solAssert(_contract.isLibrary(), "");
if (!_contract.baseContracts().empty())
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
for (auto const& var: _contract.stateVariables())
if (!var->isConstant())
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
}
void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
{
TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide()));
@ -697,20 +300,22 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (type(*var)->category() == Type::Category::Mapping)
{
if (!type(*var)->dataStoredIn(DataLocation::Storage))
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." );
else if (!isLibraryFunction && _function.isPublic())
m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
else
{
if (!type(*var)->canLiveOutsideStorage() && _function.isPublic())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
}
if (
type(*var)->category() == Type::Category::Mapping &&
!type(*var)->dataStoredIn(DataLocation::Storage)
)
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\".");
else if (
!type(*var)->canLiveOutsideStorage() &&
_function.visibility() > FunctionDefinition::Visibility::Internal
)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
if (
_function.visibility() > FunctionDefinition::Visibility::Internal &&
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(*var))
)
@ -775,15 +380,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
)
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
// Variables can be declared without type (with "var"), in which case the first assignment
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript.
// type is filled either by ReferencesResolver directly from the type name or by
// TypeChecker at the VariableDeclarationStatement level.
TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Failed to infer variable type.");
solAssert(!!varType, "Variable type not provided.");
if (_variable.value())
expectType(*_variable.value(), *varType);
@ -812,11 +412,25 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (!varType->canLiveOutsideStorage())
m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage.");
}
else if (
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
!FunctionType(_variable).interfaceFunctionType()
)
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
else if (_variable.visibility() >= VariableDeclaration::Visibility::Public)
{
FunctionType getter(_variable);
if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
{
vector<string> unsupportedTypes;
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
if (!typeSupportedByOldABIEncoder(*param))
unsupportedTypes.emplace_back(param->toString());
if (!unsupportedTypes.empty())
m_errorReporter.typeError(_variable.location(),
"The following types are only supported for getters in the new experimental ABI encoder: " +
joinHumanReadable(unsupportedTypes) +
". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature."
);
}
if (!getter.interfaceFunctionType())
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
}
switch (varType->category())
{
@ -953,7 +567,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
// External references have already been resolved in a prior stage and stored in the annotation.
// We run the resolve step again regardless.
yul::ExternalIdentifierAccess::Resolver identifierAccess = [&](
assembly::Identifier const& _identifier,
yul::Identifier const& _identifier,
yul::IdentifierContext _context,
bool
)
@ -1038,13 +652,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return size_t(1);
};
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
assembly::AsmAnalyzer analyzer(
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
yul::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
m_evmVersion,
Error::Type::SyntaxError,
assembly::AsmFlavour::Loose,
yul::AsmFlavour::Loose,
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@ -1590,6 +1204,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
if (!inlineArrayType)
m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
else if (!inlineArrayType->canLiveOutsideStorage())
m_errorReporter.fatalTypeError(_tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage.");
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
}
else
@ -1806,26 +1423,6 @@ void TypeChecker::typeCheckFunctionCall(
"\"staticcall\" is not supported by the VM version."
);
// Check for deprecated function names
if (_functionType->kind() == FunctionType::Kind::KECCAK256)
{
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
if (functionName->name() == "sha3")
m_errorReporter.typeError(
_functionCall.location(),
"\"sha3\" has been deprecated in favour of \"keccak256\""
);
}
else if (_functionType->kind() == FunctionType::Kind::Selfdestruct)
{
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
if (functionName->name() == "suicide")
m_errorReporter.typeError(
_functionCall.location(),
"\"suicide\" has been deprecated in favour of \"selfdestruct\""
);
}
// Check for event outside of emit statement
if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event)
m_errorReporter.typeError(
@ -2488,7 +2085,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
}
else
{
expectType(*index, IntegerType(256));
expectType(*index, IntegerType::uint256());
if (!m_errorReporter.hasErrors())
if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
{
@ -2519,7 +2116,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
else
{
expectType(*index, IntegerType(256));
expectType(*index, IntegerType::uint256());
if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get()))
resultType = make_shared<TypeType>(make_shared<ArrayType>(
DataLocation::Memory,
@ -2538,7 +2135,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted.");
else
{
if (!expectType(*index, IntegerType(256)))
if (!expectType(*index, IntegerType::uint256()))
m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer.");
if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
if (bytesType.numBytes() <= integerType->literalValue(nullptr))
@ -2621,6 +2218,23 @@ bool TypeChecker::visit(Identifier const& _identifier)
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
if (dynamic_cast<FunctionType const*>(annotation.type.get()))
annotation.isPure = true;
// Check for deprecated function names.
// The check is done here for the case without an actual function call.
if (FunctionType const* fType = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256)
m_errorReporter.typeError(
_identifier.location(),
"\"sha3\" has been deprecated in favour of \"keccak256\""
);
else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct)
m_errorReporter.typeError(
_identifier.location(),
"\"suicide\" has been deprecated in favour of \"selfdestruct\""
);
}
return false;
}

View File

@ -22,20 +22,23 @@
#pragma once
#include <libsolidity/interface/EVMVersion.h>
#include <liblangutil/EVMVersion.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace langutil
{
class ErrorReporter;
}
namespace dev
{
namespace solidity
{
class ErrorReporter;
/**
* The module that performs type analysis on the AST, checks the applicability of operations on
* those types and stores errors for invalid operations.
@ -45,7 +48,7 @@ class TypeChecker: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter):
TypeChecker(EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter):
m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter)
{}
@ -62,28 +65,7 @@ public:
private:
virtual bool visit(ContractDefinition const& _contract) override;
/// Checks that two functions defined in this contract with the same name have different
/// arguments and that there is at most one constructor.
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
void checkContractDuplicateEvents(ContractDefinition const& _contract);
void checkContractIllegalOverrides(ContractDefinition const& _contract);
/// Reports a type error with an appropriate message if overridden function signature differs.
/// Also stores the direct super function in the AST annotations.
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
void checkContractAbstractFunctions(ContractDefinition const& _contract);
void checkContractBaseConstructorArguments(ContractDefinition const& _contract);
void annotateBaseConstructorArguments(
ContractDefinition const& _currentContract,
FunctionDefinition const* _baseConstructor,
ASTNode const* _argumentNode
);
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
/// Checks that all requirements for a library are fulfilled if this is a library.
void checkLibraryRequirements(ContractDefinition const& _contract);
bool visit(ContractDefinition const& _contract) override;
/// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage.
/// Should only be called if the left hand side is tuple-typed.
void checkDoubleStorageAssignment(Assignment const& _assignment);
@ -122,40 +104,37 @@ private:
FunctionTypePointer _functionType
);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(VariableDeclaration const& _variable) override;
void endVisit(InheritanceSpecifier const& _inheritance) override;
void endVisit(UsingForDirective const& _usingFor) override;
bool visit(StructDefinition const& _struct) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(VariableDeclaration const& _variable) override;
/// We need to do this manually because we want to pass the bases of the current contract in
/// case this is a base constructor call.
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
virtual bool visit(EventDefinition const& _eventDef) override;
virtual void endVisit(FunctionTypeName const& _funType) override;
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(ForStatement const& _forStatement) override;
virtual void endVisit(Return const& _return) override;
virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; }
virtual void endVisit(EmitStatement const& _emit) override;
virtual bool visit(VariableDeclarationStatement const& _variable) override;
virtual void endVisit(ExpressionStatement const& _statement) override;
virtual bool visit(Conditional const& _conditional) override;
virtual bool visit(Assignment const& _assignment) override;
virtual bool visit(TupleExpression const& _tuple) override;
virtual void endVisit(BinaryOperation const& _operation) override;
virtual bool visit(UnaryOperation const& _operation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
virtual void endVisit(NewExpression const& _newExpression) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess const& _indexAccess) override;
virtual bool visit(Identifier const& _identifier) override;
virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
virtual void endVisit(Literal const& _literal) override;
template <class T>
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
bool visit(EventDefinition const& _eventDef) override;
void endVisit(FunctionTypeName const& _funType) override;
bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
bool visit(ForStatement const& _forStatement) override;
void endVisit(Return const& _return) override;
bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; }
void endVisit(EmitStatement const& _emit) override;
bool visit(VariableDeclarationStatement const& _variable) override;
void endVisit(ExpressionStatement const& _statement) override;
bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override;
bool visit(TupleExpression const& _tuple) override;
void endVisit(BinaryOperation const& _operation) override;
bool visit(UnaryOperation const& _operation) override;
bool visit(FunctionCall const& _functionCall) override;
void endVisit(NewExpression const& _newExpression) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(IndexAccess const& _indexAccess) override;
bool visit(Identifier const& _identifier) override;
void endVisit(ElementaryTypeNameExpression const& _expr) override;
void endVisit(Literal const& _literal) override;
bool contractDependenciesAreCyclic(
ContractDefinition const& _contract,
@ -183,7 +162,7 @@ private:
/// Flag indicating whether we are currently inside a StructDefinition.
bool m_insideStruct = false;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
};
}

View File

@ -19,13 +19,16 @@
#include <libevmasm/SemanticInformation.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libyul/AsmData.h>
#include <liblangutil/ErrorReporter.h>
#include <functional>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
namespace
@ -37,48 +40,48 @@ public:
explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability):
m_reportMutability(_reportMutability) {}
void operator()(assembly::Label const&) { }
void operator()(assembly::Instruction const& _instruction)
void operator()(yul::Label const&) { }
void operator()(yul::Instruction const& _instruction)
{
checkInstruction(_instruction.location, _instruction.instruction);
}
void operator()(assembly::Literal const&) {}
void operator()(assembly::Identifier const&) {}
void operator()(assembly::FunctionalInstruction const& _instr)
void operator()(yul::Literal const&) {}
void operator()(yul::Identifier const&) {}
void operator()(yul::FunctionalInstruction const& _instr)
{
checkInstruction(_instr.location, _instr.instruction);
for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg);
}
void operator()(assembly::ExpressionStatement const& _expr)
void operator()(yul::ExpressionStatement const& _expr)
{
boost::apply_visitor(*this, _expr.expression);
}
void operator()(assembly::StackAssignment const&) {}
void operator()(assembly::Assignment const& _assignment)
void operator()(yul::StackAssignment const&) {}
void operator()(yul::Assignment const& _assignment)
{
boost::apply_visitor(*this, *_assignment.value);
}
void operator()(assembly::VariableDeclaration const& _varDecl)
void operator()(yul::VariableDeclaration const& _varDecl)
{
if (_varDecl.value)
boost::apply_visitor(*this, *_varDecl.value);
}
void operator()(assembly::FunctionDefinition const& _funDef)
void operator()(yul::FunctionDefinition const& _funDef)
{
(*this)(_funDef.body);
}
void operator()(assembly::FunctionCall const& _funCall)
void operator()(yul::FunctionCall const& _funCall)
{
for (auto const& arg: _funCall.arguments)
boost::apply_visitor(*this, arg);
}
void operator()(assembly::If const& _if)
void operator()(yul::If const& _if)
{
boost::apply_visitor(*this, *_if.condition);
(*this)(_if.body);
}
void operator()(assembly::Switch const& _switch)
void operator()(yul::Switch const& _switch)
{
boost::apply_visitor(*this, *_switch.expression);
for (auto const& _case: _switch.cases)
@ -88,14 +91,14 @@ public:
(*this)(_case.body);
}
}
void operator()(assembly::ForLoop const& _for)
void operator()(yul::ForLoop const& _for)
{
(*this)(_for.pre);
boost::apply_visitor(*this, *_for.condition);
(*this)(_for.body);
(*this)(_for.post);
}
void operator()(assembly::Block const& _block)
void operator()(yul::Block const& _block)
{
for (auto const& s: _block.statements)
boost::apply_visitor(*this, s);

View File

@ -21,11 +21,15 @@
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <map>
#include <memory>
namespace langutil
{
class ErrorReporter;
struct SourceLocation;
}
namespace dev
{
namespace solidity
@ -34,7 +38,7 @@ namespace solidity
class ViewPureChecker: private ASTConstVisitor
{
public:
ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, ErrorReporter& _errorReporter):
ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, langutil::ErrorReporter& _errorReporter):
m_ast(_ast), m_errorReporter(_errorReporter) {}
bool check();
@ -43,34 +47,34 @@ private:
struct MutabilityAndLocation
{
StateMutability mutability;
SourceLocation location;
langutil::SourceLocation location;
};
virtual bool visit(FunctionDefinition const& _funDef) override;
virtual void endVisit(FunctionDefinition const& _funDef) override;
virtual bool visit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(Identifier const& _identifier) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual void endVisit(IndexAccess const& _indexAccess) override;
virtual void endVisit(ModifierInvocation const& _modifier) override;
virtual void endVisit(FunctionCall const& _functionCall) override;
virtual void endVisit(InlineAssembly const& _inlineAssembly) override;
bool visit(FunctionDefinition const& _funDef) override;
void endVisit(FunctionDefinition const& _funDef) override;
bool visit(ModifierDefinition const& _modifierDef) override;
void endVisit(ModifierDefinition const& _modifierDef) override;
void endVisit(Identifier const& _identifier) override;
bool visit(MemberAccess const& _memberAccess) override;
void endVisit(MemberAccess const& _memberAccess) override;
void endVisit(IndexAccess const& _indexAccess) override;
void endVisit(ModifierInvocation const& _modifier) override;
void endVisit(FunctionCall const& _functionCall) override;
void endVisit(InlineAssembly const& _inlineAssembly) override;
/// Called when an element of mutability @a _mutability is encountered.
/// Creates appropriate warnings and errors and sets @a m_currentBestMutability.
void reportMutability(
StateMutability _mutability,
SourceLocation const& _location,
boost::optional<SourceLocation> const& _nestedLocation = {}
langutil::SourceLocation const& _location,
boost::optional<langutil::SourceLocation> const& _nestedLocation = {}
);
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
ErrorReporter& m_errorReporter;
langutil::ErrorReporter& m_errorReporter;
bool m_errors = false;
MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, SourceLocation()};
MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()};
FunctionDefinition const* m_currentFunction = nullptr;
std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability;
};

View File

@ -23,13 +23,13 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTEnums.h>
#include <libevmasm/SourceLocation.h>
#include <liblangutil/SourceLocation.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/FixedHash.h>
@ -41,6 +41,12 @@
#include <vector>
#include <memory>
namespace yul
{
// Forward-declaration to <yul/AsmData.h>
struct Block;
}
namespace dev
{
namespace solidity
@ -58,6 +64,8 @@ class ASTConstVisitor;
class ASTNode: private boost::noncopyable
{
public:
using SourceLocation = langutil::SourceLocation;
explicit ASTNode(SourceLocation const& _location);
virtual ~ASTNode();
@ -126,9 +134,9 @@ public:
SourceUnit(SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_location), m_nodes(_nodes) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual SourceUnitAnnotation& annotation() const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
SourceUnitAnnotation& annotation() const override;
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
@ -242,8 +250,8 @@ public:
): ASTNode(_location), m_tokens(_tokens), m_literals(_literals)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<Token> const& tokens() const { return m_tokens; }
std::vector<ASTString> const& literals() const { return m_literals; }
@ -279,17 +287,17 @@ public:
m_symbolAliases(_symbolAliases)
{ }
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ASTString const& path() const { return *m_path; }
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> const& symbolAliases() const
{
return m_symbolAliases;
}
virtual ImportAnnotation& annotation() const override;
ImportAnnotation& annotation() const override;
virtual TypePointer type() const override;
TypePointer type() const override;
private:
ASTPointer<ASTString> m_path;
@ -375,8 +383,8 @@ public:
m_contractKind(_contractKind)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
@ -407,9 +415,9 @@ public:
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
virtual TypePointer type() const override;
TypePointer type() const override;
virtual ContractDefinitionAnnotation& annotation() const override;
ContractDefinitionAnnotation& annotation() const override;
ContractKind contractKind() const { return m_contractKind; }
@ -434,8 +442,8 @@ public:
):
ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
UserDefinedTypeName const& name() const { return *m_baseName; }
// Returns nullptr if no argument list was given (``C``).
@ -463,8 +471,8 @@ public:
):
ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
UserDefinedTypeName const& libraryName() const { return *m_libraryName; }
/// @returns the type name the library is attached to, null for `*`.
@ -485,14 +493,14 @@ public:
):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
virtual TypePointer type() const override;
TypePointer type() const override;
virtual TypeDeclarationAnnotation& annotation() const override;
TypeDeclarationAnnotation& annotation() const override;
private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
@ -507,14 +515,14 @@ public:
std::vector<ASTPointer<EnumValue>> const& _members
):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; }
virtual TypePointer type() const override;
TypePointer type() const override;
virtual TypeDeclarationAnnotation& annotation() const override;
TypeDeclarationAnnotation& annotation() const override;
private:
std::vector<ASTPointer<EnumValue>> m_members;
@ -529,10 +537,10 @@ public:
EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
Declaration(_location, _name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
virtual TypePointer type() const override;
TypePointer type() const override;
};
/**
@ -548,8 +556,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _parameters
):
ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters; }
@ -610,8 +618,8 @@ public:
m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
@ -620,11 +628,11 @@ public:
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { solAssert(m_body, ""); return *m_body; }
virtual bool isVisibleInContract() const override
bool isVisibleInContract() const override
{
return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
}
virtual bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); }
bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); }
/// @returns the external signature of the function
/// That consists of the name of the function followed by the types of the
@ -633,13 +641,13 @@ public:
ContractDefinition::ContractKind inContractKind() const;
virtual TypePointer type() const override;
TypePointer type() const override;
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
/// @returns null when it is not accessible as a function.
virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual FunctionDefinitionAnnotation& annotation() const override;
FunctionDefinitionAnnotation& annotation() const override;
private:
StateMutability m_stateMutability;
@ -676,14 +684,14 @@ public:
m_isConstant(_isConstant),
m_location(_referenceLocation) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
TypeName* typeName() const { return m_typeName.get(); }
ASTPointer<Expression> const& value() const { return m_value; }
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
bool isLValue() const override;
bool isPartOfExternalInterface() const override { return isPublic(); }
/// @returns true iff this variable is the parameter (or return parameter) of a function
/// (or function type name or event) or declared inside a function body.
@ -717,13 +725,13 @@ public:
/// @returns a set of allowed storage locations for the variable.
std::set<Location> allowedDataLocations() const;
virtual TypePointer type() const override;
TypePointer type() const override;
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
/// @returns null when it is not accessible as a function.
virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual VariableDeclarationAnnotation& annotation() const override;
VariableDeclarationAnnotation& annotation() const override;
protected:
Visibility defaultVisibility() const override { return Visibility::Internal; }
@ -758,14 +766,14 @@ public:
{
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Block const& body() const { return *m_body; }
virtual TypePointer type() const override;
TypePointer type() const override;
virtual ModifierDefinitionAnnotation& annotation() const override;
ModifierDefinitionAnnotation& annotation() const override;
private:
ASTPointer<Block> m_body;
@ -784,8 +792,8 @@ public:
):
ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& name() const { return m_modifierName; }
// Returns nullptr if no argument list was given (``mod``).
@ -817,15 +825,15 @@ public:
{
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
bool isAnonymous() const { return m_anonymous; }
virtual TypePointer type() const override;
virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
TypePointer type() const override;
FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual EventDefinitionAnnotation& annotation() const override;
EventDefinitionAnnotation& annotation() const override;
private:
bool m_anonymous = false;
@ -840,21 +848,21 @@ class MagicVariableDeclaration: public Declaration
public:
MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type):
Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override
void accept(ASTVisitor&) override
{
solAssert(false, "MagicVariableDeclaration used inside real AST.");
}
virtual void accept(ASTConstVisitor&) const override
void accept(ASTConstVisitor&) const override
{
solAssert(false, "MagicVariableDeclaration used inside real AST.");
}
virtual FunctionTypePointer functionType(bool) const override
FunctionTypePointer functionType(bool) const override
{
solAssert(m_type->category() == Type::Category::Function, "");
return std::dynamic_pointer_cast<FunctionType const>(m_type);
}
virtual TypePointer type() const override { return m_type; }
TypePointer type() const override { return m_type; }
private:
std::shared_ptr<Type const> m_type;
@ -872,7 +880,7 @@ protected:
explicit TypeName(SourceLocation const& _location): ASTNode(_location) {}
public:
virtual TypeNameAnnotation& annotation() const override;
TypeNameAnnotation& annotation() const override;
};
/**
@ -891,8 +899,8 @@ public:
solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ElementaryTypeNameToken const& typeName() const { return m_type; }
@ -911,12 +919,12 @@ class UserDefinedTypeName: public TypeName
public:
UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath):
TypeName(_location), m_namePath(_namePath) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTString> const& namePath() const { return m_namePath; }
virtual UserDefinedTypeNameAnnotation& annotation() const override;
UserDefinedTypeNameAnnotation& annotation() const override;
private:
std::vector<ASTString> m_namePath;
@ -938,8 +946,8 @@ public:
TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
m_visibility(_visibility), m_stateMutability(_stateMutability)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& parameterTypes() const { return m_parameterTypes->parameters(); }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameterTypes() const { return m_returnTypes->parameters(); }
@ -972,8 +980,8 @@ public:
ASTPointer<TypeName> const& _valueType
):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ElementaryTypeName const& keyType() const { return *m_keyType; }
TypeName const& valueType() const { return *m_valueType; }
@ -995,8 +1003,8 @@ public:
ASTPointer<Expression> const& _length
):
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
TypeName const& baseType() const { return *m_baseType; }
Expression const* length() const { return m_length.get(); }
@ -1023,15 +1031,9 @@ public:
ASTPointer<ASTString> const& _docString
): ASTNode(_location), Documented(_docString) {}
virtual StatementAnnotation& annotation() const override;
StatementAnnotation& annotation() const override;
};
namespace assembly
{
// Forward-declaration to AsmData.h
struct Block;
}
/**
* Inline assembly.
*/
@ -1041,18 +1043,18 @@ public:
InlineAssembly(
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
std::shared_ptr<assembly::Block> const& _operations
std::shared_ptr<yul::Block> const& _operations
):
Statement(_location, _docString), m_operations(_operations) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
assembly::Block const& operations() const { return *m_operations; }
yul::Block const& operations() const { return *m_operations; }
virtual InlineAssemblyAnnotation& annotation() const override;
InlineAssemblyAnnotation& annotation() const override;
private:
std::shared_ptr<assembly::Block> m_operations;
std::shared_ptr<yul::Block> m_operations;
};
/**
@ -1067,8 +1069,8 @@ public:
std::vector<ASTPointer<Statement>> const& _statements
):
Statement(_location, _docString), m_statements(_statements) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<Statement>> const& statements() const { return m_statements; }
@ -1088,8 +1090,8 @@ public:
ASTPointer<ASTString> const& _docString
): Statement(_location, _docString) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
/**
@ -1111,8 +1113,8 @@ public:
m_trueBody(_trueBody),
m_falseBody(_falseBody)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& condition() const { return *m_condition; }
Statement const& trueStatement() const { return *m_trueBody; }
@ -1149,8 +1151,8 @@ public:
):
BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body),
m_isDoWhile(_isDoWhile) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& condition() const { return *m_condition; }
Statement const& body() const { return *m_body; }
@ -1182,8 +1184,8 @@ public:
m_loopExpression(_loopExpression),
m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Statement const* initializationExpression() const { return m_initExpression.get(); }
Expression const* condition() const { return m_condExpression.get(); }
@ -1206,8 +1208,8 @@ class Continue: public Statement
public:
explicit Continue(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
class Break: public Statement
@ -1215,8 +1217,8 @@ class Break: public Statement
public:
explicit Break(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
class Return: public Statement
@ -1227,12 +1229,12 @@ public:
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> _expression
): Statement(_location, _docString), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const* expression() const { return m_expression.get(); }
virtual ReturnAnnotation& annotation() const override;
ReturnAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression; ///< value to return, optional
@ -1246,8 +1248,8 @@ class Throw: public Statement
public:
explicit Throw(SourceLocation const& _location, ASTPointer<ASTString> const& _docString):
Statement(_location, _docString) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
};
/**
@ -1262,8 +1264,8 @@ public:
ASTPointer<FunctionCall> const& _functionCall
):
Statement(_location, _docString), m_eventCall(_functionCall) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
FunctionCall const& eventCall() const { return *m_eventCall; }
@ -1289,8 +1291,8 @@ public:
ASTPointer<Expression> const& _initialValue
):
Statement(_location, _docString), m_variables(_variables), m_initialValue(_initialValue) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* initialValue() const { return m_initialValue.get(); }
@ -1317,8 +1319,8 @@ public:
ASTPointer<Expression> _expression
):
Statement(_location, _docString), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
@ -1358,8 +1360,8 @@ public:
m_trueExpression(_trueExpression),
m_falseExpression(_falseExpression)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& condition() const { return *m_condition; }
Expression const& trueExpression() const { return *m_trueExpression; }
@ -1389,8 +1391,8 @@ public:
{
solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftHandSide() const { return *m_leftHandSide; }
Token assignmentOperator() const { return m_assigmentOperator; }
@ -1421,8 +1423,8 @@ public:
Expression(_location),
m_components(_components),
m_isArray(_isArray) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<Expression>> const& components() const { return m_components; }
bool isInlineArray() const { return m_isArray; }
@ -1452,8 +1454,8 @@ public:
{
solAssert(TokenTraits::isUnaryOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Token getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
@ -1482,8 +1484,8 @@ public:
{
solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftExpression() const { return *m_left; }
Expression const& rightExpression() const { return *m_right; }
@ -1510,14 +1512,14 @@ public:
std::vector<ASTPointer<ASTString>> const& _names
):
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
virtual FunctionCallAnnotation& annotation() const override;
FunctionCallAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
@ -1537,8 +1539,8 @@ public:
ASTPointer<TypeName> const& _typeName
):
Expression(_location), m_typeName(_typeName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
TypeName const& typeName() const { return *m_typeName; }
@ -1558,12 +1560,12 @@ public:
ASTPointer<ASTString> const& _memberName
):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
ASTString const& memberName() const { return *m_memberName; }
virtual MemberAccessAnnotation& annotation() const override;
MemberAccessAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
@ -1582,8 +1584,8 @@ public:
ASTPointer<Expression> const& _index
):
Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& baseExpression() const { return *m_base; }
Expression const* indexExpression() const { return m_index.get(); }
@ -1614,12 +1616,12 @@ public:
ASTPointer<ASTString> const& _name
):
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ASTString const& name() const { return *m_name; }
virtual IdentifierAnnotation& annotation() const override;
IdentifierAnnotation& annotation() const override;
private:
ASTPointer<ASTString> m_name;
@ -1636,8 +1638,8 @@ public:
ElementaryTypeNameExpression(SourceLocation const& _location, ElementaryTypeNameToken const& _type):
PrimaryExpression(_location), m_typeToken(_type)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
ElementaryTypeNameToken const& typeName() const { return m_typeToken; }
@ -1672,8 +1674,8 @@ public:
SubDenomination _sub = SubDenomination::None
):
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Token token() const { return m_token; }
/// @returns the non-parsed value of the literal

View File

@ -30,6 +30,12 @@
#include <vector>
#include <set>
namespace yul
{
struct AsmAnalysisInfo;
struct Identifier;
}
namespace dev
{
namespace solidity
@ -120,12 +126,6 @@ struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation
{
};
namespace assembly
{
struct AsmAnalysisInfo;
struct Identifier;
}
struct InlineAssemblyAnnotation: StatementAnnotation
{
struct ExternalIdentifierInfo
@ -137,9 +137,9 @@ struct InlineAssemblyAnnotation: StatementAnnotation
};
/// Mapping containing resolved references to external identifiers and their value size
std::map<assembly::Identifier const*, ExternalIdentifierInfo> externalReferences;
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
/// Information generated during analysis phase.
std::shared_ptr<assembly::AsmAnalysisInfo> analysisInfo;
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
};
struct ReturnAnnotation: StatementAnnotation

View File

@ -21,7 +21,7 @@
#pragma once
#include <libsolidity/interface/Exceptions.h>
#include <liblangutil/Exceptions.h>
#include <string>

View File

@ -23,10 +23,11 @@
#include <boost/algorithm/string/join.hpp>
#include <libdevcore/UTF8.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
using namespace std;
using namespace langutil;
namespace dev
{
@ -121,8 +122,8 @@ void ASTJsonConverter::setJsonNode(
string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const
{
int sourceIndex{-1};
if (_location.sourceName && m_sourceIndices.count(*_location.sourceName))
sourceIndex = m_sourceIndices.at(*_location.sourceName);
if (_location.source && m_sourceIndices.count(_location.source->name()))
sourceIndex = m_sourceIndices.at(_location.source->name());
int length = -1;
if (_location.start >= 0 && _location.end >= 0)
length = _location.end - _location.start;
@ -171,7 +172,7 @@ void ASTJsonConverter::appendExpressionAttributes(
_attributes += exprAttributes;
}
Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
{
Json::Value tuple(Json::objectValue);
tuple["src"] = sourceLocationToString(_info.first->location);
@ -464,7 +465,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
}
}
setJsonNode(_node, "InlineAssembly", {
make_pair("operations", Json::Value(assembly::AsmPrinter()(_node.operations()))),
make_pair("operations", Json::Value(yul::AsmPrinter()(_node.operations()))),
make_pair("externalReferences", std::move(externalReferences))
});
return false;

View File

@ -25,10 +25,15 @@
#include <ostream>
#include <stack>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/Exceptions.h>
#include <liblangutil/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <json/json.h>
namespace langutil
{
struct SourceLocation;
}
namespace dev
{
namespace solidity
@ -120,7 +125,7 @@ private:
std::string const& _nodeName,
std::vector<std::pair<std::string, Json::Value>>&& _attributes
);
std::string sourceLocationToString(SourceLocation const& _location) const;
std::string sourceLocationToString(langutil::SourceLocation const& _location) const;
static std::string namePathToString(std::vector<ASTString> const& _namePath);
static Json::Value idOrNull(ASTNode const* _pt)
{
@ -130,7 +135,7 @@ private:
{
return _node ? toJson(*_node) : Json::nullValue;
}
Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const;
Json::Value inlineAssemblyIdentifierToJson(std::pair<yul::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const;
static std::string location(VariableDeclaration::Location _location);
static std::string contractKind(ContractDefinition::ContractKind _kind);
static std::string functionCallKind(FunctionCallKind _kind);

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