mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
c8a2cb6283
@ -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
|
||||
|
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
41
Changelog.md
41
Changelog.md
@ -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:
|
||||
|
@ -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).
|
||||
|
@ -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.
|
||||
|
@ -612,5 +612,9 @@
|
||||
"0.5.0": {
|
||||
"bugs": [],
|
||||
"released": "2018-11-13"
|
||||
},
|
||||
"0.5.1": {
|
||||
"bugs": [],
|
||||
"released": "2018-12-03"
|
||||
}
|
||||
}
|
@ -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>`_,
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
-----------------------
|
||||
|
||||
|
@ -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?
|
||||
======================================================================
|
||||
|
||||
|
@ -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 )*
|
||||
|
@ -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
|
||||
========
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
::
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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>`_
|
||||
|
@ -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:
|
||||
|
||||
::
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
=====================
|
||||
|
@ -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**:
|
||||
|
@ -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``.
|
||||
|
||||
|
52
docs/yul.rst
52
docs/yul.rst
@ -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))
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -117,6 +117,8 @@ int AssemblyItem::returnValues() const
|
||||
|
||||
bool AssemblyItem::canBeFunctional() const
|
||||
{
|
||||
if (m_jumpType != JumpType::Ordinary)
|
||||
return false;
|
||||
switch (m_type)
|
||||
{
|
||||
case Operation:
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -30,6 +30,7 @@
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::eth;
|
||||
using namespace langutil;
|
||||
|
||||
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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++));
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -29,6 +29,7 @@
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::eth;
|
||||
using namespace langutil;
|
||||
|
||||
ostream& KnownState::stream(ostream& _out) const
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <libevmasm/GasMeter.h>
|
||||
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
13
liblangutil/CMakeLists.txt
Normal file
13
liblangutil/CMakeLists.txt
Normal 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
108
liblangutil/CharStream.cpp
Normal 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
100
liblangutil/CharStream.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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)
|
||||
{
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
@ -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>;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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:
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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
376
liblangutil/Token.h
Normal 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);
|
||||
};
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
#include <libsolidity/interface/EVMVersion.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -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})
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
463
libsolidity/analysis/ContractLevelChecker.cpp
Normal file
463
libsolidity/analysis/ContractLevelChecker.cpp
Normal 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");
|
||||
}
|
87
libsolidity/analysis/ContractLevelChecker.h
Normal file
87
libsolidity/analysis/ContractLevelChecker.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -22,8 +22,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <libsolidity/parsing/Token.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user