mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
This commit is contained in:
commit
e239f62b64
@ -627,14 +627,14 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
# DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh
|
# DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh
|
||||||
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
|
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
|
||||||
- run:
|
- run:
|
||||||
name: Install build dependencies
|
name: Install build dependencies
|
||||||
command: ./.circleci/osx_install_dependencies.sh
|
command: ./.circleci/osx_install_dependencies.sh
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
paths:
|
paths:
|
||||||
- /usr/local/bin
|
- /usr/local/bin
|
||||||
- /usr/local/sbin
|
- /usr/local/sbin
|
||||||
@ -663,7 +663,7 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
- run: *run_soltest
|
- run: *run_soltest
|
||||||
@ -679,7 +679,7 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
- run: *run_cmdline_tests
|
- run: *run_cmdline_tests
|
||||||
@ -886,14 +886,14 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- dependencies-win-{{ checksum "scripts/install_deps.ps1" }}
|
- dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }}
|
||||||
# DO NOT EDIT between here and save_cache, but rather edit .\scripts\install_deps.ps1
|
# DO NOT EDIT between here and save_cache, but rather edit .\scripts\install_deps.ps1
|
||||||
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
|
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
|
||||||
- run:
|
- run:
|
||||||
name: "Installing dependencies"
|
name: "Installing dependencies"
|
||||||
command: .\scripts\install_deps.ps1
|
command: .\scripts\install_deps.ps1
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: dependencies-win-{{ checksum "scripts/install_deps.ps1" }}
|
key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }}
|
||||||
paths:
|
paths:
|
||||||
- .\deps
|
- .\deps
|
||||||
- run:
|
- run:
|
||||||
|
@ -52,9 +52,6 @@ function validate_checksum {
|
|||||||
|
|
||||||
if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
|
if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
|
||||||
then
|
then
|
||||||
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
|
|
||||||
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
|
|
||||||
brew update
|
|
||||||
brew unlink python
|
brew unlink python
|
||||||
brew install boost
|
brew install boost
|
||||||
brew install cmake
|
brew install cmake
|
||||||
|
@ -41,6 +41,7 @@ include(EthCcache)
|
|||||||
|
|
||||||
# Let's find our dependencies
|
# Let's find our dependencies
|
||||||
include(EthDependencies)
|
include(EthDependencies)
|
||||||
|
include(fmtlib)
|
||||||
include(jsoncpp)
|
include(jsoncpp)
|
||||||
include(range-v3)
|
include(range-v3)
|
||||||
include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR})
|
include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR})
|
||||||
|
@ -13,12 +13,21 @@ Language Features:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Commandline Interface: Accept nested brackets in step sequences passed to ``--yul-optimizations``.
|
||||||
|
* Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
||||||
* SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions.
|
* SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions.
|
||||||
|
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
|
||||||
|
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* Code Generator: Fix constructor source mappings for immutables.
|
||||||
* Commandline Interface: Fix extra newline character being appended to sources passed through standard input, affecting their hashes.
|
* Commandline Interface: Fix extra newline character being appended to sources passed through standard input, affecting their hashes.
|
||||||
|
* Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them.
|
||||||
|
* Commandline Interface: Don't return zero exit code when writing linked files to disk fails.
|
||||||
* SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``).
|
* SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``).
|
||||||
|
* TypeChecker: Fix internal error when using user defined value types in public library functions.
|
||||||
|
* Yul IR Generator: Do not output empty switches/if-bodies for empty contracts.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
20
cmake/fmtlib.cmake
Normal file
20
cmake/fmtlib.cmake
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
fmtlib
|
||||||
|
PREFIX "${CMAKE_BINARY_DIR}/deps"
|
||||||
|
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
|
||||||
|
DOWNLOAD_NAME fmt-8.0.1.tar.gz
|
||||||
|
URL https://github.com/fmtlib/fmt/archive/8.0.1.tar.gz
|
||||||
|
URL_HASH SHA256=b06ca3130158c625848f3fb7418f235155a4d389b2abc3a6245fb01cb0eb1e01
|
||||||
|
)
|
||||||
|
|
||||||
|
if (CMAKE_VERSION VERSION_LESS "3.14.0")
|
||||||
|
FetchContent_GetProperties(fmtlib)
|
||||||
|
if (NOT fmtlib_POPULATED)
|
||||||
|
FetchContent_Populate(fmtlib)
|
||||||
|
add_subdirectory(${fmtlib_SOURCE_DIR} ${fmtlib_BINARY_DIR})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
FetchContent_MakeAvailable(fmtlib)
|
||||||
|
endif()
|
72
docs/_static/css/custom.css
vendored
72
docs/_static/css/custom.css
vendored
@ -12,3 +12,75 @@ pre {
|
|||||||
.rst-content table.docutils td {
|
.rst-content table.docutils td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* links */
|
||||||
|
.rst-content a:not(:visited) {
|
||||||
|
color: #002fa7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rst-content .highlighted {
|
||||||
|
background: #eac545;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* code block highlights */
|
||||||
|
.rst-content pre {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wy-side-nav-search img.logo {
|
||||||
|
width: 100px !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wy-side-nav-search > a {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* project version (displayed under project logo) */
|
||||||
|
.wy-side-nav-search .version {
|
||||||
|
color: #272525 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* menu section headers */
|
||||||
|
.wy-menu .caption {
|
||||||
|
color: #65afff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link to Remix IDE shown next to code snippets */
|
||||||
|
p.remix-link-container {
|
||||||
|
position: relative;
|
||||||
|
right: -100%; /* Positioned next to the the top-right corner of the code block following it. */
|
||||||
|
}
|
||||||
|
|
||||||
|
a.remix-link {
|
||||||
|
position: absolute; /* Remove it from normal flow not to affect the original position of the snippet. */
|
||||||
|
top: 0.5em;
|
||||||
|
width: 3.236em; /* Size of the margin (= right-side padding in .wy-nav-content in the current theme). */
|
||||||
|
}
|
||||||
|
|
||||||
|
a.remix-link .link-icon {
|
||||||
|
background: url("../img/solid-share-arrow.svg") no-repeat;
|
||||||
|
display: block;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.remix-link .link-text {
|
||||||
|
display: none; /* Visible only on hover. */
|
||||||
|
width: 3.3em; /* Narrow enough to get two lines of text. */
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: normal;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.remix-link:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.remix-link:hover .link-text {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
21
docs/_static/css/dark.css
vendored
21
docs/_static/css/dark.css
vendored
@ -1,11 +1,9 @@
|
|||||||
/* links */
|
/* links */
|
||||||
|
|
||||||
a,
|
.rst-content a:not(:visited) {
|
||||||
a:visited {
|
color: #aaddff !important;
|
||||||
color: #aaddff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* code directives */
|
/* code directives */
|
||||||
|
|
||||||
.method dt,
|
.method dt,
|
||||||
@ -46,6 +44,9 @@ em.property {
|
|||||||
background-color: #5a5a5a;
|
background-color: #5a5a5a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rst-content pre {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* inlined code highlights */
|
/* inlined code highlights */
|
||||||
|
|
||||||
@ -70,6 +71,12 @@ code.docutils.literal.notranslate {
|
|||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* highlight color search text */
|
||||||
|
|
||||||
|
.rst-content .highlighted {
|
||||||
|
background: #ff5722;
|
||||||
|
box-shadow: 0 0 0 2px #f0978b;
|
||||||
|
}
|
||||||
|
|
||||||
/* notes, warnings, hints */
|
/* notes, warnings, hints */
|
||||||
|
|
||||||
@ -620,3 +627,9 @@ code.docutils.literal.notranslate {
|
|||||||
|
|
||||||
|
|
||||||
/* Literal.Number.Integer.Long */
|
/* Literal.Number.Integer.Long */
|
||||||
|
|
||||||
|
|
||||||
|
/* Link to Remix IDE shown over code snippets */
|
||||||
|
a.remix-link {
|
||||||
|
filter: invert(1); /* The icon is black. In dark mode we want it white. */
|
||||||
|
}
|
||||||
|
6
docs/_static/css/toggle.css
vendored
6
docs/_static/css/toggle.css
vendored
@ -75,3 +75,9 @@ html.transition *:after {
|
|||||||
transition: ease-in-out 200ms !important;
|
transition: ease-in-out 200ms !important;
|
||||||
transition-delay: 0 !important;
|
transition-delay: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.wy-nav-side {
|
||||||
|
/* The default padding of 2em is too small and the "Keyword Index" link gets obscured
|
||||||
|
* by the version toggle. */
|
||||||
|
padding-bottom: 3em;
|
||||||
|
}
|
||||||
|
1
docs/_static/img/solid-share-arrow.svg
vendored
Normal file
1
docs/_static/img/solid-share-arrow.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="share" class="svg-inline--fa fa-share fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z"></path></svg>
|
After Width: | Height: | Size: 547 B |
7
docs/_templates/footer.html
vendored
Normal file
7
docs/_templates/footer.html
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends "!footer.html" %}
|
||||||
|
|
||||||
|
{% block extrafooter %}
|
||||||
|
<p>
|
||||||
|
<a href="{{ pathto('credits-and-attribution') }}">Credits and attribution</a>.
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
4
docs/_templates/layout.html
vendored
4
docs/_templates/layout.html
vendored
@ -2,5 +2,9 @@
|
|||||||
|
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
<a href="{{ pathto('genindex') }}">Keyword Index</a>
|
<a href="{{ pathto('genindex') }}">Keyword Index</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -130,6 +130,12 @@ Global Variables
|
|||||||
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||||
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
When contracts are evaluated off-chain rather than in context of a transaction included in a
|
||||||
|
block, you should not assume that ``block.*`` and ``tx.*`` refer to values from any specific
|
||||||
|
block or transaction. These values are provided by the EVM implementation that executes the
|
||||||
|
contract and can be arbitrary.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
||||||
unless you know what you are doing.
|
unless you know what you are doing.
|
||||||
|
@ -44,6 +44,7 @@ def setup(sphinx):
|
|||||||
extensions = [
|
extensions = [
|
||||||
'sphinx_a4doc',
|
'sphinx_a4doc',
|
||||||
'html_extra_template_renderer',
|
'html_extra_template_renderer',
|
||||||
|
'remix_code_links',
|
||||||
]
|
]
|
||||||
|
|
||||||
a4_base_path = os.path.dirname(__file__) + '/grammar'
|
a4_base_path = os.path.dirname(__file__) + '/grammar'
|
||||||
@ -128,7 +129,11 @@ html_theme = 'sphinx_rtd_theme'
|
|||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#html_theme_options = {}
|
html_theme_options = {
|
||||||
|
'logo_only': True,
|
||||||
|
'style_nav_header_background': '#65afff',
|
||||||
|
'display_version': True,
|
||||||
|
}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
#html_theme_path = []
|
#html_theme_path = []
|
||||||
@ -142,7 +147,7 @@ html_theme = 'sphinx_rtd_theme'
|
|||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
#html_logo = None
|
html_logo = "logo.svg"
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
@ -86,12 +86,10 @@ Running the Compiler Tests
|
|||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Some tests require the `evmone <https://github.com/ethereum/evmone/releases>`_
|
For running all compiler tests you may want to optionally install a few
|
||||||
library, others require `libz3 <https://github.com/Z3Prover/z3>`_. The test script
|
dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_,
|
||||||
tries to discover the location of the ``evmone`` library, which can be located
|
`libz3 <https://github.com/Z3Prover/z3>`_, and
|
||||||
in the current directory, installed on the system level, or the ``deps`` folder
|
`libhera <https://github.com/ewasm/hera>`_).
|
||||||
in the project top level. The required file is called ``libevmone.so`` on Linux
|
|
||||||
systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS.
|
|
||||||
|
|
||||||
On macOS some of the testing scripts expect GNU coreutils to be installed.
|
On macOS some of the testing scripts expect GNU coreutils to be installed.
|
||||||
This can be easiest accomplished using Homebrew: ``brew install coreutils``.
|
This can be easiest accomplished using Homebrew: ``brew install coreutils``.
|
||||||
@ -108,13 +106,28 @@ including those bundled into the `Boost C++ Test Framework <https://www.boost.or
|
|||||||
application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and
|
application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and
|
||||||
compilation tests.
|
compilation tests.
|
||||||
|
|
||||||
The test system automatically tries to discover the location of the ``evmone`` library
|
The test system automatically tries to discover the location of
|
||||||
starting from the current directory. The required file is called ``libevmone.so`` on Linux systems,
|
the `evmone <https://github.com/ethereum/evmone/releases>`_ for running the semantic tests.
|
||||||
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that
|
|
||||||
use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``,
|
The ``evmone`` library must be located in the ``deps`` or ``deps/lib`` directory relative to the
|
||||||
``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
|
current working directory, to its parent or its parent's parent. Alternatively an explicit location
|
||||||
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.8.0>`_
|
for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environment variable.
|
||||||
and place it in the project root path or inside the ``deps`` folder.
|
|
||||||
|
``evmone`` is needed mainly for running semantic and gas tests.
|
||||||
|
If you do not have it installed, you can skip these tests by passing the ``--no-semantic-tests``
|
||||||
|
flag to ``scripts/soltest.sh``.
|
||||||
|
|
||||||
|
Running Ewasm tests is disabled by default and can be explicitly enabled
|
||||||
|
via ``./scripts/soltest.sh --ewasm`` and requires `hera <https://github.com/ewasm/hera>`_
|
||||||
|
to be found by ``soltest``.
|
||||||
|
The mechanism for locating the ``hera`` library is the same as for ``evmone``, except that the
|
||||||
|
variable for specifying an explicit location is called ``ETH_HERA``.
|
||||||
|
|
||||||
|
The ``evmone`` and ``hera`` libraries should both end with the file name
|
||||||
|
extension ``.so`` on Linux, ``.dll`` on Windows systems and ``.dylib`` on macOS.
|
||||||
|
|
||||||
|
For running SMT tests, the ``libz3`` library must be installed and locatable
|
||||||
|
by ``cmake`` during compiler configure stage.
|
||||||
|
|
||||||
If the ``libz3`` library is not installed on your system, you should disable the
|
If the ``libz3`` library is not installed on your system, you should disable the
|
||||||
SMT tests by exporting ``SMT_FLAGS=--no-smt`` before running ``./scripts/tests.sh`` or
|
SMT tests by exporting ``SMT_FLAGS=--no-smt`` before running ``./scripts/tests.sh`` or
|
||||||
|
21
docs/credits-and-attribution.rst
Normal file
21
docs/credits-and-attribution.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.. This page is meant to be linked to from the footer.
|
||||||
|
|
||||||
|
:orphan:
|
||||||
|
|
||||||
|
#######################
|
||||||
|
Credits and Attribution
|
||||||
|
#######################
|
||||||
|
|
||||||
|
Website icons
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. |icon-share-solid| image:: _static/img/solid-share-arrow.svg
|
||||||
|
.. _share icon: https://fontawesome.com/v5.15/icons/share?style=solid
|
||||||
|
.. _Font Awesome Free License: https://fontawesome.com/license/free
|
||||||
|
|
||||||
|
+-------------------------+-----------------------------------------------------------------------+
|
||||||
|
| Icon | Attribution |
|
||||||
|
+=========================+=======================================================================+
|
||||||
|
| |icon-share-solid| | - Source: `share icon`_ from Font Awesome 5.15.0. |
|
||||||
|
| | - License: `Font Awesome Free License`_ (CC BY 4.0). |
|
||||||
|
+-------------------------+-----------------------------------------------------------------------+
|
@ -11,7 +11,7 @@ sign and verify signatures, and setup the payment channel.
|
|||||||
Creating and verifying signatures
|
Creating and verifying signatures
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
Imagine Alice wants to send a quantity of Ether to Bob, i.e.
|
Imagine Alice wants to send some Ether to Bob, i.e.
|
||||||
Alice is the sender and Bob is the recipient.
|
Alice is the sender and Bob is the recipient.
|
||||||
|
|
||||||
Alice only needs to send cryptographically signed messages off-chain
|
Alice only needs to send cryptographically signed messages off-chain
|
||||||
|
83
docs/ext/remix_code_links.py
Normal file
83
docs/ext/remix_code_links.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import base64
|
||||||
|
import docutils # pragma pylint: disable=import-error
|
||||||
|
|
||||||
|
from sphinx.util import logging # pragma pylint: disable=import-error
|
||||||
|
|
||||||
|
# NOTE: 2000 should generally be safe for all browsers, while 8000 for most of them.
|
||||||
|
MAX_SAFE_URL_LENGTH = 10000
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def insert_node_before(child, new_sibling):
|
||||||
|
assert child in child.parent.children
|
||||||
|
|
||||||
|
for position, node in enumerate(child.parent.children):
|
||||||
|
if node == child:
|
||||||
|
child.parent.insert(position, new_sibling)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def remix_code_url(source_code, language, solidity_version):
|
||||||
|
# NOTE: base64 encoded data may contain +, = and / characters. Remix seems to handle them just
|
||||||
|
# fine without any escaping.
|
||||||
|
base64_encoded_source = base64.b64encode(source_code.encode('utf-8')).decode('ascii')
|
||||||
|
return f"https://remix.ethereum.org/?language={language}&version={solidity_version}&code={base64_encoded_source}"
|
||||||
|
|
||||||
|
|
||||||
|
def build_remix_link_node(url):
|
||||||
|
link_icon_node = docutils.nodes.inline()
|
||||||
|
link_icon_node.set_class('link-icon')
|
||||||
|
|
||||||
|
link_text_node = docutils.nodes.inline(text="open in Remix")
|
||||||
|
link_text_node.set_class('link-text')
|
||||||
|
|
||||||
|
reference_node = docutils.nodes.reference('', '', internal=False, refuri=url)
|
||||||
|
reference_node.set_class('remix-link')
|
||||||
|
reference_node += [link_icon_node, link_text_node]
|
||||||
|
|
||||||
|
paragraph_node = docutils.nodes.paragraph()
|
||||||
|
paragraph_node.set_class('remix-link-container')
|
||||||
|
paragraph_node += reference_node
|
||||||
|
return paragraph_node
|
||||||
|
|
||||||
|
|
||||||
|
def insert_remix_link(app, doctree, solidity_version):
|
||||||
|
if app.builder.format != 'html' or app.builder.name == 'epub':
|
||||||
|
return
|
||||||
|
|
||||||
|
for literal_block_node in doctree.traverse(docutils.nodes.literal_block):
|
||||||
|
assert 'language' in literal_block_node.attributes
|
||||||
|
language = literal_block_node.attributes['language'].lower()
|
||||||
|
if language in ['solidity', 'yul']:
|
||||||
|
text_nodes = list(literal_block_node.traverse(docutils.nodes.Text))
|
||||||
|
assert len(text_nodes) == 1
|
||||||
|
|
||||||
|
remix_url = remix_code_url(text_nodes[0], language, solidity_version)
|
||||||
|
url_length = len(remix_url.encode('utf-8'))
|
||||||
|
if url_length > MAX_SAFE_URL_LENGTH:
|
||||||
|
logger.warning(
|
||||||
|
"Remix URL generated from the code snippet exceeds the maximum safe URL length "
|
||||||
|
" (%d > %d bytes).",
|
||||||
|
url_length,
|
||||||
|
MAX_SAFE_URL_LENGTH,
|
||||||
|
location=(literal_block_node.source, literal_block_node.line),
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_node_before(literal_block_node, build_remix_link_node(remix_url))
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
# NOTE: Need to access _raw_config here because setup() runs before app.config is ready.
|
||||||
|
solidity_version = app.config._raw_config['version'] # pylint: disable=protected-access
|
||||||
|
|
||||||
|
app.connect(
|
||||||
|
'doctree-resolved',
|
||||||
|
lambda app, doctree, docname: insert_remix_link(app, doctree, solidity_version)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'version': solidity_version,
|
||||||
|
'parallel_read_safe': True,
|
||||||
|
'parallel_write_safe': True,
|
||||||
|
}
|
@ -1,11 +1,6 @@
|
|||||||
Solidity
|
Solidity
|
||||||
========
|
========
|
||||||
|
|
||||||
.. image:: logo.svg
|
|
||||||
:width: 120px
|
|
||||||
:alt: Solidity logo
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
Solidity is an object-oriented, high-level language for implementing smart
|
Solidity is an object-oriented, high-level language for implementing smart
|
||||||
contracts. Smart contracts are programs which govern the behaviour of accounts
|
contracts. Smart contracts are programs which govern the behaviour of accounts
|
||||||
within the Ethereum state.
|
within the Ethereum state.
|
||||||
@ -22,8 +17,10 @@ With Solidity you can create contracts for uses such as voting, crowdfunding, bl
|
|||||||
and multi-signature wallets.
|
and multi-signature wallets.
|
||||||
|
|
||||||
When deploying contracts, you should use the latest released
|
When deploying contracts, you should use the latest released
|
||||||
version of Solidity. This is because breaking changes as well as
|
version of Solidity. Apart from exceptional cases, only the latest version receives
|
||||||
new features and bug fixes are introduced regularly. We currently use
|
`security fixes<https://github.com/ethereum/solidity/security/policy#supported-versions>`.
|
||||||
|
Furthermore, breaking changes as well as
|
||||||
|
new features are introduced regularly. We currently use
|
||||||
a 0.x version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
|
a 0.x version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
@ -275,7 +275,7 @@ The following transformation steps are the main components:
|
|||||||
- Common Subexpression Eliminator
|
- Common Subexpression Eliminator
|
||||||
- Expression Simplifier
|
- Expression Simplifier
|
||||||
- Redundant Assign Eliminator
|
- Redundant Assign Eliminator
|
||||||
- Full Function Inliner
|
- Full Inliner
|
||||||
|
|
||||||
Optimizer Steps
|
Optimizer Steps
|
||||||
---------------
|
---------------
|
||||||
@ -297,7 +297,8 @@ on the individual steps and their sequence below.
|
|||||||
- :ref:`for-loop-condition-into-body`.
|
- :ref:`for-loop-condition-into-body`.
|
||||||
- :ref:`for-loop-condition-out-of-body`.
|
- :ref:`for-loop-condition-out-of-body`.
|
||||||
- :ref:`for-loop-init-rewriter`.
|
- :ref:`for-loop-init-rewriter`.
|
||||||
- :ref:`functional-inliner`.
|
- :ref:`expression-inliner`.
|
||||||
|
- :ref:`full-inliner`.
|
||||||
- :ref:`function-grouper`.
|
- :ref:`function-grouper`.
|
||||||
- :ref:`function-hoister`.
|
- :ref:`function-hoister`.
|
||||||
- :ref:`function-specializer`.
|
- :ref:`function-specializer`.
|
||||||
@ -1082,9 +1083,9 @@ The actual removal of the function is performed by the Unused Pruner.
|
|||||||
Function Inlining
|
Function Inlining
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
.. _functional-inliner:
|
.. _expression-inliner:
|
||||||
|
|
||||||
FunctionalInliner
|
ExpressionInliner
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This component of the optimizer performs restricted function inlining by inlining functions that can be
|
This component of the optimizer performs restricted function inlining by inlining functions that can be
|
||||||
@ -1107,12 +1108,12 @@ The result of this inlining is always a single expression.
|
|||||||
|
|
||||||
This component can only be used on sources with unique names.
|
This component can only be used on sources with unique names.
|
||||||
|
|
||||||
.. _full-function-inliner:
|
.. _full-inliner:
|
||||||
|
|
||||||
FullFunctionInliner
|
FullInliner
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
The Full Function Inliner replaces certain calls of certain functions
|
The Full Inliner replaces certain calls of certain functions
|
||||||
by the function's body. This is not very helpful in most cases, because
|
by the function's body. This is not very helpful in most cases, because
|
||||||
it just increases the code size but does not have a benefit. Furthermore,
|
it just increases the code size but does not have a benefit. Furthermore,
|
||||||
code is usually very expensive and we would often rather have shorter
|
code is usually very expensive and we would often rather have shorter
|
||||||
|
@ -64,3 +64,7 @@ This means the following source mappings represent the same information:
|
|||||||
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
|
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
|
||||||
|
|
||||||
``1:2:1;:9;2:1:2;;``
|
``1:2:1;:9;2:1:2;;``
|
||||||
|
|
||||||
|
Important to note is that when the :ref:`verbatim <yul-verbatim>` builtin is used,
|
||||||
|
the source mappings will be invalid: The builtin is considered a single
|
||||||
|
instruction instead of potentially multiple.
|
||||||
|
@ -14,7 +14,12 @@ hashes or sent as the data of a message call. Similarly, before
|
|||||||
storing a value in the storage, the remaining bits need to be cleaned
|
storing a value in the storage, the remaining bits need to be cleaned
|
||||||
because otherwise the garbled value can be observed.
|
because otherwise the garbled value can be observed.
|
||||||
|
|
||||||
On the other hand, we do not clean the bits if the immediately
|
Note that access via inline assembly is not considered such an operation:
|
||||||
|
If you use inline assembly to access Solidity variables
|
||||||
|
shorter than 256 bits, the compiler does not guarantee that
|
||||||
|
the value is properly cleaned up.
|
||||||
|
|
||||||
|
Moreover, we do not clean the bits if the immediately
|
||||||
following operation is not affected. For instance, since any non-zero
|
following operation is not affected. For instance, since any non-zero
|
||||||
value is considered ``true`` by ``JUMPI`` instruction, we do not clean
|
value is considered ``true`` by ``JUMPI`` instruction, we do not clean
|
||||||
the boolean values before they are used as the condition for
|
the boolean values before they are used as the condition for
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
|
||||||
|
.. index: ir breaking changes
|
||||||
|
|
||||||
*********************************
|
*********************************
|
||||||
Solidity IR-based Codegen Changes
|
Solidity IR-based Codegen Changes
|
||||||
*********************************
|
*********************************
|
||||||
@ -35,13 +38,15 @@ hiding new and different behavior in existing code.
|
|||||||
|
|
||||||
We have the same behavior for implicit delete, for example when array of structs is shortened.
|
We have the same behavior for implicit delete, for example when array of structs is shortened.
|
||||||
|
|
||||||
- Function modifiers are implemented in a slightly different way regarding function parameters.
|
- Function modifiers are implemented in a slightly different way regarding function parameters and return variables.
|
||||||
This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier.
|
This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier.
|
||||||
In the old code generator, each function parameter has a fixed slot on the stack. If the function
|
In the old code generator, each function parameter and return variable has a fixed slot on the stack.
|
||||||
is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the
|
If the function is run multiple times because ``_;`` is used multiple times or used in a loop, then a
|
||||||
function parameter's value is visible in the next execution of the function.
|
change to the function parameter's or return variable's value is visible in the next execution of the function.
|
||||||
The new code generator implements modifiers using actual functions and passes function parameters on.
|
The new code generator implements modifiers using actual functions and passes function parameters on.
|
||||||
This means that multiple executions of a function will get the same values for the parameters.
|
This means that multiple evaluations of a function's body will get the same values for the parameters,
|
||||||
|
and the effect on return variables is that they are reset to their default (zero) value for each
|
||||||
|
execution.
|
||||||
|
|
||||||
.. code-block:: solidity
|
.. code-block:: solidity
|
||||||
|
|
||||||
@ -57,6 +62,35 @@ hiding new and different behavior in existing code.
|
|||||||
If you execute ``f(0)`` in the old code generator, it will return ``2``, while
|
If you execute ``f(0)`` in the old code generator, it will return ``2``, while
|
||||||
it will return ``1`` when using the new code generator.
|
it will return ``1`` when using the new code generator.
|
||||||
|
|
||||||
|
.. code-block:: solidity
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >=0.7.1 <0.9.0;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
bool active = true;
|
||||||
|
modifier mod()
|
||||||
|
{
|
||||||
|
_;
|
||||||
|
active = false;
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
function foo() external mod() returns (uint ret)
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
ret = 1; // Same as ``return 1``
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The function ``C.foo()`` returns the following values:
|
||||||
|
|
||||||
|
- Old code generator: ``1`` as the return variable is initialized to ``0`` only once before the first ``_;``
|
||||||
|
evaluation and then overwritten by the ``return 1;``. It is not initialized again for the second ``_;``
|
||||||
|
evaluation and ``foo()`` does not explicitly assign it either (due to ``active == false``), thus it keeps
|
||||||
|
its first value.
|
||||||
|
- New code generator: ``0`` as all parameters, including return parameters, will be re-initialized before
|
||||||
|
each ``_;`` evaluation.
|
||||||
|
|
||||||
- The order of contract initialization has changed in case of inheritance.
|
- The order of contract initialization has changed in case of inheritance.
|
||||||
|
|
||||||
The order used to be:
|
The order used to be:
|
||||||
|
@ -92,6 +92,12 @@ Block and Transaction Properties
|
|||||||
``msg.value`` can change for every **external** function call.
|
``msg.value`` can change for every **external** function call.
|
||||||
This includes calls to library functions.
|
This includes calls to library functions.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
When contracts are evaluated off-chain rather than in context of a transaction included in a
|
||||||
|
block, you should not assume that ``block.*`` and ``tx.*`` refer to values from any specific
|
||||||
|
block or transaction. These values are provided by the EVM implementation that executes the
|
||||||
|
contract and can be arbitrary.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
||||||
unless you know what you are doing.
|
unless you know what you are doing.
|
||||||
|
@ -308,7 +308,18 @@ Input Description
|
|||||||
// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects
|
// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects
|
||||||
// "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
|
// "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
|
||||||
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
|
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
|
||||||
"revertStrings": "default"
|
"revertStrings": "default",
|
||||||
|
// Optional: How much extra debug information to include in comments in the produced EVM
|
||||||
|
// assembly and Yul code. Available components are:
|
||||||
|
// - `location`: Annotations of the form `@src <index>:<start>:<end>` indicating the
|
||||||
|
// location of the corresponding element in the original Solidity file, where:
|
||||||
|
// - `<index>` is the file index matching the `@use-src` annotation,
|
||||||
|
// - `<start>` is the index of the first byte at that location,
|
||||||
|
// - `<end>` is the index of the first byte after that location.
|
||||||
|
// - `snippet`: A single-line code snippet from the location indicated by `@src`.
|
||||||
|
// The snippet is quoted and follows the corresponding `@src` annotation.
|
||||||
|
// - `*`: Wildcard value that can be used to request everything.
|
||||||
|
"debugInfo": ["location", "snippet"]
|
||||||
},
|
},
|
||||||
// Metadata settings (optional)
|
// Metadata settings (optional)
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
@ -1002,6 +1002,8 @@ within one Yul subobject. If at least one ``memoryguard`` call is found in a sub
|
|||||||
the additional optimiser steps will be run on it.
|
the additional optimiser steps will be run on it.
|
||||||
|
|
||||||
|
|
||||||
|
.. _yul-verbatim:
|
||||||
|
|
||||||
verbatim
|
verbatim
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
@ -1263,6 +1265,7 @@ Abbreviation Full name
|
|||||||
``a`` ``SSATransform``
|
``a`` ``SSATransform``
|
||||||
``t`` ``StructuralSimplifier``
|
``t`` ``StructuralSimplifier``
|
||||||
``u`` ``UnusedPruner``
|
``u`` ``UnusedPruner``
|
||||||
|
``p`` ``UnusedFunctionParameterPruner``
|
||||||
``d`` ``VarDeclInitializer``
|
``d`` ``VarDeclInitializer``
|
||||||
============ ===============================
|
============ ===============================
|
||||||
|
|
||||||
|
@ -48,11 +48,11 @@ using namespace solidity::evmasm;
|
|||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
AssemblyItem const& Assembly::append(AssemblyItem _i)
|
||||||
{
|
{
|
||||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||||
m_deposit += static_cast<int>(_i.deposit());
|
m_deposit += static_cast<int>(_i.deposit());
|
||||||
m_items.emplace_back(_i);
|
m_items.emplace_back(move(_i));
|
||||||
if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid())
|
if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid())
|
||||||
m_items.back().setLocation(m_currentSourceLocation);
|
m_items.back().setLocation(m_currentSourceLocation);
|
||||||
m_items.back().m_modifierDepth = m_currentModifierDepth;
|
m_items.back().m_modifierDepth = m_currentModifierDepth;
|
||||||
@ -68,7 +68,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
|
|||||||
ret += i.second.size();
|
ret += i.second.size();
|
||||||
|
|
||||||
for (AssemblyItem const& i: m_items)
|
for (AssemblyItem const& i: m_items)
|
||||||
ret += i.bytesRequired(tagSize);
|
ret += i.bytesRequired(tagSize, Precision::Approximate);
|
||||||
if (numberEncodingSize(ret) <= tagSize)
|
if (numberEncodingSize(ret) <= tagSize)
|
||||||
return static_cast<unsigned>(ret);
|
return static_cast<unsigned>(ret);
|
||||||
}
|
}
|
||||||
@ -96,13 +96,13 @@ public:
|
|||||||
m_out(_out), m_prefix(_prefix), m_sourceCodes(_sourceCodes), m_assembly(_assembly)
|
m_out(_out), m_prefix(_prefix), m_sourceCodes(_sourceCodes), m_assembly(_assembly)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void feed(AssemblyItem const& _item)
|
void feed(AssemblyItem const& _item, DebugInfoSelection const& _debugInfoSelection)
|
||||||
{
|
{
|
||||||
if (_item.location().isValid() && _item.location() != m_location)
|
if (_item.location().isValid() && _item.location() != m_location)
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
m_location = _item.location();
|
m_location = _item.location();
|
||||||
printLocation();
|
printLocation(_debugInfoSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
string expression = _item.toAssemblyText(m_assembly);
|
string expression = _item.toAssemblyText(m_assembly);
|
||||||
@ -142,16 +142,29 @@ public:
|
|||||||
m_pending.clear();
|
m_pending.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void printLocation()
|
void printLocation(DebugInfoSelection const& _debugInfoSelection)
|
||||||
{
|
{
|
||||||
if (!m_location.isValid())
|
if (!m_location.isValid() || (!_debugInfoSelection.location && !_debugInfoSelection.snippet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_out << m_prefix << " /*";
|
m_out << m_prefix << " /*";
|
||||||
|
|
||||||
|
if (_debugInfoSelection.location)
|
||||||
|
{
|
||||||
if (m_location.sourceName)
|
if (m_location.sourceName)
|
||||||
m_out << " " + escapeAndQuoteString(*m_location.sourceName);
|
m_out << " " + escapeAndQuoteString(*m_location.sourceName);
|
||||||
if (m_location.hasText())
|
if (m_location.hasText())
|
||||||
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
|
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
|
||||||
m_out << " " << locationFromSources(m_sourceCodes, m_location);
|
}
|
||||||
|
|
||||||
|
if (_debugInfoSelection.snippet)
|
||||||
|
{
|
||||||
|
if (_debugInfoSelection.location)
|
||||||
|
m_out << " ";
|
||||||
|
|
||||||
|
m_out << locationFromSources(m_sourceCodes, m_location);
|
||||||
|
}
|
||||||
|
|
||||||
m_out << " */" << endl;
|
m_out << " */" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,12 +180,17 @@ private:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap const& _sourceCodes) const
|
void Assembly::assemblyStream(
|
||||||
|
ostream& _out,
|
||||||
|
DebugInfoSelection const& _debugInfoSelection,
|
||||||
|
string const& _prefix,
|
||||||
|
StringMap const& _sourceCodes
|
||||||
|
) const
|
||||||
{
|
{
|
||||||
Functionalizer f(_out, _prefix, _sourceCodes, *this);
|
Functionalizer f(_out, _prefix, _sourceCodes, *this);
|
||||||
|
|
||||||
for (auto const& i: m_items)
|
for (auto const& i: m_items)
|
||||||
f.feed(i);
|
f.feed(i, _debugInfoSelection);
|
||||||
f.flush();
|
f.flush();
|
||||||
|
|
||||||
if (!m_data.empty() || !m_subs.empty())
|
if (!m_data.empty() || !m_subs.empty())
|
||||||
@ -185,7 +203,7 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co
|
|||||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||||
{
|
{
|
||||||
_out << endl << _prefix << "sub_" << i << ": assembly {\n";
|
_out << endl << _prefix << "sub_" << i << ": assembly {\n";
|
||||||
m_subs[i]->assemblyStream(_out, _prefix + " ", _sourceCodes);
|
m_subs[i]->assemblyStream(_out, _debugInfoSelection, _prefix + " ", _sourceCodes);
|
||||||
_out << _prefix << "}" << endl;
|
_out << _prefix << "}" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,10 +212,13 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co
|
|||||||
_out << endl << _prefix << "auxdata: 0x" << util::toHex(m_auxiliaryData) << endl;
|
_out << endl << _prefix << "auxdata: 0x" << util::toHex(m_auxiliaryData) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
string Assembly::assemblyString(StringMap const& _sourceCodes) const
|
string Assembly::assemblyString(
|
||||||
|
DebugInfoSelection const& _debugInfoSelection,
|
||||||
|
StringMap const& _sourceCodes
|
||||||
|
) const
|
||||||
{
|
{
|
||||||
ostringstream tmp;
|
ostringstream tmp;
|
||||||
assemblyStream(tmp, "", _sourceCodes);
|
assemblyStream(tmp, _debugInfoSelection, "", _sourceCodes);
|
||||||
return tmp.str();
|
return tmp.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,8 +696,11 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
break;
|
break;
|
||||||
case PushImmutable:
|
case PushImmutable:
|
||||||
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::PUSH32));
|
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::PUSH32));
|
||||||
|
// Maps keccak back to the "identifier" string of that immutable.
|
||||||
ret.immutableReferences[i.data()].first = m_immutables.at(i.data());
|
ret.immutableReferences[i.data()].first = m_immutables.at(i.data());
|
||||||
|
// Record the bytecode offset of the PUSH32 argument.
|
||||||
ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size());
|
ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size());
|
||||||
|
// Advance bytecode by 32 bytes (default initialized).
|
||||||
ret.bytecode.resize(ret.bytecode.size() + 32);
|
ret.bytecode.resize(ret.bytecode.size() + 32);
|
||||||
break;
|
break;
|
||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
@ -684,6 +708,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
break;
|
break;
|
||||||
case AssignImmutable:
|
case AssignImmutable:
|
||||||
{
|
{
|
||||||
|
// Expect 2 elements on stack (source, dest_base)
|
||||||
auto const& offsets = immutableReferencesBySub[i.data()].second;
|
auto const& offsets = immutableReferencesBySub[i.data()].second;
|
||||||
for (size_t i = 0; i < offsets.size(); ++i)
|
for (size_t i = 0; i < offsets.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
|
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
@ -64,7 +65,7 @@ public:
|
|||||||
AssemblyItem newPushImmutable(std::string const& _identifier);
|
AssemblyItem newPushImmutable(std::string const& _identifier);
|
||||||
AssemblyItem newImmutableAssignment(std::string const& _identifier);
|
AssemblyItem newImmutableAssignment(std::string const& _identifier);
|
||||||
|
|
||||||
AssemblyItem const& append(AssemblyItem const& _i);
|
AssemblyItem const& append(AssemblyItem _i);
|
||||||
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
||||||
|
|
||||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||||
@ -142,10 +143,12 @@ public:
|
|||||||
|
|
||||||
/// Create a text representation of the assembly.
|
/// Create a text representation of the assembly.
|
||||||
std::string assemblyString(
|
std::string assemblyString(
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
StringMap const& _sourceCodes = StringMap()
|
StringMap const& _sourceCodes = StringMap()
|
||||||
) const;
|
) const;
|
||||||
void assemblyStream(
|
void assemblyStream(
|
||||||
std::ostream& _out,
|
std::ostream& _out,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
std::string const& _prefix = "",
|
std::string const& _prefix = "",
|
||||||
StringMap const& _sourceCodes = StringMap()
|
StringMap const& _sourceCodes = StringMap()
|
||||||
) const;
|
) const;
|
||||||
|
@ -65,7 +65,7 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
|
|||||||
setData(data);
|
setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AssemblyItem::bytesRequired(size_t _addressLength) const
|
size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) const
|
||||||
{
|
{
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
@ -87,10 +87,25 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength) const
|
|||||||
case PushImmutable:
|
case PushImmutable:
|
||||||
return 1 + 32;
|
return 1 + 32;
|
||||||
case AssignImmutable:
|
case AssignImmutable:
|
||||||
if (m_immutableOccurrences)
|
{
|
||||||
return 1 + (3 + 32) * *m_immutableOccurrences;
|
unsigned long immutableOccurrences = 0;
|
||||||
|
|
||||||
|
// Skip exact immutables count if no precise count was requested
|
||||||
|
if (_precision == Precision::Approximate)
|
||||||
|
immutableOccurrences = 1; // Assume one immut. ref.
|
||||||
else
|
else
|
||||||
return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways.
|
{
|
||||||
|
solAssert(m_immutableOccurrences, "No immutable references. `bytesRequired()` called before assembly()?");
|
||||||
|
immutableOccurrences = m_immutableOccurrences.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (immutableOccurrences != 0)
|
||||||
|
// (DUP DUP PUSH <n> ADD MSTORE)* (PUSH <n> ADD MSTORE)
|
||||||
|
return (immutableOccurrences - 1) * (5 + 32) + (3 + 32);
|
||||||
|
else
|
||||||
|
// POP POP
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
case VerbatimBytecode:
|
case VerbatimBytecode:
|
||||||
return std::get<2>(*m_verbatimBytecode).size();
|
return std::get<2>(*m_verbatimBytecode).size();
|
||||||
default:
|
default:
|
||||||
@ -322,6 +337,26 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
return _out;
|
return _out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t AssemblyItem::opcodeCount() const noexcept
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case AssemblyItemType::AssignImmutable:
|
||||||
|
// Append empty items if this AssignImmutable was referenced more than once.
|
||||||
|
// For n immutable occurrences the first (n - 1) occurrences will
|
||||||
|
// generate 5 opcodes and the last will generate 3 opcodes,
|
||||||
|
// because it is reusing the 2 top-most elements on the stack.
|
||||||
|
solAssert(m_immutableOccurrences, "");
|
||||||
|
|
||||||
|
if (m_immutableOccurrences.value() != 0)
|
||||||
|
return (*m_immutableOccurrences - 1) * 5 + 3;
|
||||||
|
else
|
||||||
|
return 2; // two POP's
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string AssemblyItem::computeSourceMapping(
|
std::string AssemblyItem::computeSourceMapping(
|
||||||
AssemblyItems const& _items,
|
AssemblyItems const& _items,
|
||||||
map<string, unsigned> const& _sourceIndicesMap
|
map<string, unsigned> const& _sourceIndicesMap
|
||||||
@ -334,6 +369,7 @@ std::string AssemblyItem::computeSourceMapping(
|
|||||||
int prevSourceIndex = -1;
|
int prevSourceIndex = -1;
|
||||||
int prevModifierDepth = -1;
|
int prevModifierDepth = -1;
|
||||||
char prevJump = 0;
|
char prevJump = 0;
|
||||||
|
|
||||||
for (auto const& item: _items)
|
for (auto const& item: _items)
|
||||||
{
|
{
|
||||||
if (!ret.empty())
|
if (!ret.empty())
|
||||||
@ -402,6 +438,9 @@ std::string AssemblyItem::computeSourceMapping(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.opcodeCount() > 1)
|
||||||
|
ret += string(item.opcodeCount() - 1, ';');
|
||||||
|
|
||||||
prevStart = location.start;
|
prevStart = location.start;
|
||||||
prevLength = length;
|
prevLength = length;
|
||||||
prevSourceIndex = sourceIndex;
|
prevSourceIndex = sourceIndex;
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libsolutil/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
|
#include <optional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -51,6 +52,8 @@ enum AssemblyItemType
|
|||||||
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
|
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Precision { Precise , Approximate };
|
||||||
|
|
||||||
class Assembly;
|
class Assembly;
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
using AssemblyItems = std::vector<AssemblyItem>;
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
@ -147,7 +150,10 @@ public:
|
|||||||
|
|
||||||
/// @returns an upper bound for the number of bytes required by this item, assuming that
|
/// @returns an upper bound for the number of bytes required by this item, assuming that
|
||||||
/// the value of a jump tag takes @a _addressLength bytes.
|
/// the value of a jump tag takes @a _addressLength bytes.
|
||||||
size_t bytesRequired(size_t _addressLength) const;
|
/// @param _precision Whether to return a precise count (which involves
|
||||||
|
/// counting immutable references which are only set after
|
||||||
|
/// a call to `assemble()`) or an approx. count.
|
||||||
|
size_t bytesRequired(size_t _addressLength, Precision _precision = Precision::Precise) const;
|
||||||
size_t arguments() const;
|
size_t arguments() const;
|
||||||
size_t returnValues() const;
|
size_t returnValues() const;
|
||||||
size_t deposit() const { return returnValues() - arguments(); }
|
size_t deposit() const { return returnValues() - arguments(); }
|
||||||
@ -169,9 +175,11 @@ public:
|
|||||||
|
|
||||||
size_t m_modifierDepth = 0;
|
size_t m_modifierDepth = 0;
|
||||||
|
|
||||||
void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared<size_t>(_n); }
|
void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = _n; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
size_t opcodeCount() const noexcept;
|
||||||
|
|
||||||
AssemblyItemType m_type;
|
AssemblyItemType m_type;
|
||||||
Instruction m_instruction; ///< Only valid if m_type == Operation
|
Instruction m_instruction; ///< Only valid if m_type == Operation
|
||||||
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
|
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
|
||||||
@ -184,14 +192,14 @@ private:
|
|||||||
/// e.g. PushSubSize, PushTag, PushSub, etc.
|
/// e.g. PushSubSize, PushTag, PushSub, etc.
|
||||||
mutable std::shared_ptr<u256> m_pushedValue;
|
mutable std::shared_ptr<u256> m_pushedValue;
|
||||||
/// Number of PushImmutable's with the same hash. Only used for AssignImmutable.
|
/// Number of PushImmutable's with the same hash. Only used for AssignImmutable.
|
||||||
mutable std::shared_ptr<size_t> m_immutableOccurrences;
|
mutable std::optional<size_t> m_immutableOccurrences;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength)
|
inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength, Precision _precision = Precision::Precise)
|
||||||
{
|
{
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (AssemblyItem const& item: _items)
|
for (AssemblyItem const& item: _items)
|
||||||
size += item.bytesRequired(_addressLength);
|
size += item.bytesRequired(_addressLength, _precision);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,4 +38,4 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(evmasm ${sources})
|
add_library(evmasm ${sources})
|
||||||
target_link_libraries(evmasm PUBLIC solutil)
|
target_link_libraries(evmasm PUBLIC solutil fmt::fmt-header-only)
|
||||||
|
@ -103,7 +103,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
|
|||||||
|
|
||||||
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
||||||
{
|
{
|
||||||
return evmasm::bytesRequired(_items, 3); // assume 3 byte addresses
|
return evmasm::bytesRequired(_items, 3, Precision::Approximate); // assume 3 byte addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstantOptimisationMethod::replaceConstants(
|
void ConstantOptimisationMethod::replaceConstants(
|
||||||
|
@ -63,7 +63,7 @@ template<typename RangeType>
|
|||||||
uint64_t codeSize(RangeType const& _itemRange)
|
uint64_t codeSize(RangeType const& _itemRange)
|
||||||
{
|
{
|
||||||
return ranges::accumulate(_itemRange | ranges::views::transform(
|
return ranges::accumulate(_itemRange | ranges::views::transform(
|
||||||
[](auto const& _item) { return _item.bytesRequired(2); }
|
[](auto const& _item) { return _item.bytesRequired(2, Precision::Approximate); }
|
||||||
), 0u);
|
), 0u);
|
||||||
}
|
}
|
||||||
/// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.
|
/// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.
|
||||||
|
@ -388,6 +388,8 @@ size_t numberOfPops(AssemblyItems const& _items)
|
|||||||
|
|
||||||
bool PeepholeOptimiser::optimise()
|
bool PeepholeOptimiser::optimise()
|
||||||
{
|
{
|
||||||
|
// Avoid referencing immutables too early by using approx. counting in bytesRequired()
|
||||||
|
auto const approx = evmasm::Precision::Approximate;
|
||||||
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
|
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
|
||||||
while (state.i < m_items.size())
|
while (state.i < m_items.size())
|
||||||
applyMethods(
|
applyMethods(
|
||||||
@ -398,7 +400,7 @@ bool PeepholeOptimiser::optimise()
|
|||||||
);
|
);
|
||||||
if (m_optimisedItems.size() < m_items.size() || (
|
if (m_optimisedItems.size() < m_items.size() || (
|
||||||
m_optimisedItems.size() == m_items.size() && (
|
m_optimisedItems.size() == m_items.size() && (
|
||||||
evmasm::bytesRequired(m_optimisedItems, 3) < evmasm::bytesRequired(m_items, 3) ||
|
evmasm::bytesRequired(m_optimisedItems, 3, approx) < evmasm::bytesRequired(m_items, 3, approx) ||
|
||||||
numberOfPops(m_optimisedItems) > numberOfPops(m_items)
|
numberOfPops(m_optimisedItems) > numberOfPops(m_items)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
@ -3,6 +3,8 @@ set(sources
|
|||||||
Common.h
|
Common.h
|
||||||
CharStream.cpp
|
CharStream.cpp
|
||||||
CharStream.h
|
CharStream.h
|
||||||
|
DebugInfoSelection.cpp
|
||||||
|
DebugInfoSelection.h
|
||||||
ErrorReporter.cpp
|
ErrorReporter.cpp
|
||||||
ErrorReporter.h
|
ErrorReporter.h
|
||||||
EVMVersion.h
|
EVMVersion.h
|
||||||
@ -29,4 +31,4 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(langutil ${sources})
|
add_library(langutil ${sources})
|
||||||
target_link_libraries(langutil PUBLIC solutil)
|
target_link_libraries(langutil PUBLIC solutil fmt::fmt-header-only)
|
||||||
|
157
liblangutil/DebugInfoSelection.cpp
Normal file
157
liblangutil/DebugInfoSelection.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
|
||||||
|
#include <range/v3/range/conversion.hpp>
|
||||||
|
#include <range/v3/view/map.hpp>
|
||||||
|
#include <range/v3/view/split.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
DebugInfoSelection const DebugInfoSelection::All(bool _value) noexcept
|
||||||
|
{
|
||||||
|
DebugInfoSelection result;
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
result.*member = _value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfoSelection const DebugInfoSelection::Only(bool DebugInfoSelection::* _member) noexcept
|
||||||
|
{
|
||||||
|
DebugInfoSelection result{};
|
||||||
|
result.*_member = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<DebugInfoSelection> DebugInfoSelection::fromString(string_view _input)
|
||||||
|
{
|
||||||
|
// TODO: Make more stuff constexpr and make it a static_assert().
|
||||||
|
solAssert(componentMap().count("all") == 0, "");
|
||||||
|
solAssert(componentMap().count("none") == 0, "");
|
||||||
|
|
||||||
|
if (_input == "all")
|
||||||
|
return All();
|
||||||
|
if (_input == "none")
|
||||||
|
return None();
|
||||||
|
|
||||||
|
return fromComponents(_input | ranges::views::split(',') | ranges::to<vector<string>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<DebugInfoSelection> DebugInfoSelection::fromComponents(
|
||||||
|
vector<string> const& _componentNames,
|
||||||
|
bool _acceptWildcards
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(componentMap().count("*") == 0, "");
|
||||||
|
|
||||||
|
DebugInfoSelection selection;
|
||||||
|
for (auto const& component: _componentNames)
|
||||||
|
{
|
||||||
|
if (component == "*")
|
||||||
|
return (_acceptWildcards ? make_optional(DebugInfoSelection::All()) : nullopt);
|
||||||
|
|
||||||
|
if (!selection.enable(component))
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugInfoSelection::enable(string _component)
|
||||||
|
{
|
||||||
|
auto memberIt = componentMap().find(boost::trim_copy(_component));
|
||||||
|
if (memberIt == componentMap().end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this->*(memberIt->second) = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugInfoSelection::any() const noexcept
|
||||||
|
{
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
if (this->*member)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugInfoSelection::all() const noexcept
|
||||||
|
{
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
if (!(this->*member))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfoSelection& DebugInfoSelection::operator&=(DebugInfoSelection const& _other)
|
||||||
|
{
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
this->*member &= _other.*member;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfoSelection& DebugInfoSelection::operator|=(DebugInfoSelection const& _other)
|
||||||
|
{
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
this->*member |= _other.*member;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfoSelection DebugInfoSelection::operator&(DebugInfoSelection _other) const noexcept
|
||||||
|
{
|
||||||
|
_other &= *this;
|
||||||
|
return _other;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfoSelection DebugInfoSelection::operator|(DebugInfoSelection _other) const noexcept
|
||||||
|
{
|
||||||
|
_other |= *this;
|
||||||
|
return _other;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebugInfoSelection::operator==(DebugInfoSelection const& _other) const noexcept
|
||||||
|
{
|
||||||
|
for (bool DebugInfoSelection::* member: componentMap() | ranges::views::values)
|
||||||
|
if (this->*member != _other.*member)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream& langutil::operator<<(ostream& _stream, DebugInfoSelection const& _selection)
|
||||||
|
{
|
||||||
|
vector<string> selectedComponentNames;
|
||||||
|
for (auto const& [name, member]: _selection.componentMap())
|
||||||
|
if (_selection.*member)
|
||||||
|
selectedComponentNames.push_back(name);
|
||||||
|
|
||||||
|
return _stream << joinHumanReadable(selectedComponentNames, ",");
|
||||||
|
}
|
86
liblangutil/DebugInfoSelection.h
Normal file
86
liblangutil/DebugInfoSelection.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
/**
|
||||||
|
* Handles selections of debug info components.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::langutil
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a set of flags corresponding to components of debug info selected for some purpose.
|
||||||
|
*
|
||||||
|
* Provides extra functionality for enumerating the components and serializing/deserializing the
|
||||||
|
* selection to/from a comma-separated string.
|
||||||
|
*/
|
||||||
|
struct DebugInfoSelection
|
||||||
|
{
|
||||||
|
static DebugInfoSelection const All(bool _value = true) noexcept;
|
||||||
|
static DebugInfoSelection const None() noexcept { return All(false); }
|
||||||
|
static DebugInfoSelection const Only(bool DebugInfoSelection::* _member) noexcept;
|
||||||
|
static DebugInfoSelection const Default() noexcept { return All(); }
|
||||||
|
|
||||||
|
static std::optional<DebugInfoSelection> fromString(std::string_view _input);
|
||||||
|
static std::optional<DebugInfoSelection> fromComponents(
|
||||||
|
std::vector<std::string> const& _componentNames,
|
||||||
|
bool _acceptWildcards = false
|
||||||
|
);
|
||||||
|
bool enable(std::string _component);
|
||||||
|
|
||||||
|
bool all() const noexcept;
|
||||||
|
bool any() const noexcept;
|
||||||
|
bool none() const noexcept { return !any(); }
|
||||||
|
bool only(bool DebugInfoSelection::* _member) const noexcept { return *this == Only(_member); }
|
||||||
|
|
||||||
|
DebugInfoSelection& operator&=(DebugInfoSelection const& _other);
|
||||||
|
DebugInfoSelection& operator|=(DebugInfoSelection const& _other);
|
||||||
|
DebugInfoSelection operator&(DebugInfoSelection _other) const noexcept;
|
||||||
|
DebugInfoSelection operator|(DebugInfoSelection _other) const noexcept;
|
||||||
|
|
||||||
|
bool operator!=(DebugInfoSelection const& _other) const noexcept { return !(*this == _other); }
|
||||||
|
bool operator==(DebugInfoSelection const& _other) const noexcept;
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection);
|
||||||
|
|
||||||
|
static auto const& componentMap()
|
||||||
|
{
|
||||||
|
static std::map<std::string, bool DebugInfoSelection::*> const components = {
|
||||||
|
{"location", &DebugInfoSelection::location},
|
||||||
|
{"snippet", &DebugInfoSelection::snippet},
|
||||||
|
{"ast-id", &DebugInfoSelection::astID},
|
||||||
|
};
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool location = false; ///< Include source location. E.g. `@src 3:50:100`
|
||||||
|
bool snippet = false; ///< Include source code snippet next to location. E.g. `@src 3:50:100 "contract C {..."`
|
||||||
|
bool astID = false; ///< Include ID of the Solidity AST node. E.g. `@ast-id 15`
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& _stream, DebugInfoSelection const& _selection);
|
||||||
|
|
||||||
|
}
|
@ -59,6 +59,13 @@ struct SourceLocation
|
|||||||
return start <= _other.start && _other.end <= end;
|
return start <= _other.start && _other.end <= end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool containsOffset(int _pos) const
|
||||||
|
{
|
||||||
|
if (!hasText() || _pos < 0)
|
||||||
|
return false;
|
||||||
|
return start <= _pos && _pos < end;
|
||||||
|
}
|
||||||
|
|
||||||
bool intersects(SourceLocation const& _other) const
|
bool intersects(SourceLocation const& _other) const
|
||||||
{
|
{
|
||||||
if (!hasText() || !_other.hasText() || !equalSources(_other))
|
if (!hasText() || !_other.hasText() || !equalSources(_other))
|
||||||
|
@ -159,4 +159,4 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(solidity ${sources})
|
add_library(solidity ${sources})
|
||||||
target_link_libraries(solidity PUBLIC yul evmasm langutil smtutil solutil Boost::boost)
|
target_link_libraries(solidity PUBLIC yul evmasm langutil smtutil solutil Boost::boost fmt::fmt-header-only)
|
||||||
|
@ -533,12 +533,12 @@ void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall)
|
|||||||
yul::ASTWalker::operator()(_functionCall);
|
yul::ASTWalker::operator()(_functionCall);
|
||||||
|
|
||||||
if (auto const *builtinFunction = m_inlineAssembly->dialect().builtin(_functionCall.functionName.name))
|
if (auto const *builtinFunction = m_inlineAssembly->dialect().builtin(_functionCall.functionName.name))
|
||||||
if (builtinFunction->controlFlowSideEffects.terminates)
|
|
||||||
{
|
{
|
||||||
if (builtinFunction->controlFlowSideEffects.reverts)
|
if (builtinFunction->controlFlowSideEffects.canTerminate)
|
||||||
connect(m_currentNode, m_revertNode);
|
|
||||||
else
|
|
||||||
connect(m_currentNode, m_transactionReturnNode);
|
connect(m_currentNode, m_transactionReturnNode);
|
||||||
|
if (builtinFunction->controlFlowSideEffects.canRevert)
|
||||||
|
connect(m_currentNode, m_revertNode);
|
||||||
|
if (!builtinFunction->controlFlowSideEffects.canContinue)
|
||||||
m_currentNode = newLabel();
|
m_currentNode = newLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include <libsolutil/Algorithms.h>
|
#include <libsolutil/Algorithms.h>
|
||||||
#include <libsolutil/FunctionSelector.h>
|
#include <libsolutil/FunctionSelector.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/map.hpp>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -246,7 +246,10 @@ public:
|
|||||||
|
|
||||||
/// @returns the declared name.
|
/// @returns the declared name.
|
||||||
ASTString const& name() const { return *m_name; }
|
ASTString const& name() const { return *m_name; }
|
||||||
|
|
||||||
|
/// @returns the location of the declared name itself or empty location if not available or unknown.
|
||||||
SourceLocation const& nameLocation() const noexcept { return m_nameLocation; }
|
SourceLocation const& nameLocation() const noexcept { return m_nameLocation; }
|
||||||
|
|
||||||
bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; }
|
bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; }
|
||||||
Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; }
|
Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; }
|
||||||
bool isPublic() const { return visibility() >= Visibility::Public; }
|
bool isPublic() const { return visibility() >= Visibility::Public; }
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/range/algorithm/copy.hpp>
|
|
||||||
|
|
||||||
#include <range/v3/view/enumerate.hpp>
|
#include <range/v3/view/enumerate.hpp>
|
||||||
#include <range/v3/view/reverse.hpp>
|
#include <range/v3/view/reverse.hpp>
|
||||||
@ -2563,6 +2562,11 @@ string UserDefinedValueType::toString(bool /* _short */) const
|
|||||||
return *definition().annotation().canonicalName;
|
return *definition().annotation().canonicalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string UserDefinedValueType::canonicalName() const
|
||||||
|
{
|
||||||
|
return *definition().annotation().canonicalName;
|
||||||
|
}
|
||||||
|
|
||||||
vector<tuple<string, Type const*>> UserDefinedValueType::makeStackItems() const
|
vector<tuple<string, Type const*>> UserDefinedValueType::makeStackItems() const
|
||||||
{
|
{
|
||||||
return underlyingType().stackItems();
|
return underlyingType().stackItems();
|
||||||
|
@ -1141,7 +1141,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override { solAssert(false, ""); }
|
std::string canonicalName() const override;
|
||||||
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
|
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -135,11 +135,15 @@ string dispenseLocationComment(langutil::SourceLocation const& _location, IRGene
|
|||||||
{
|
{
|
||||||
solAssert(_location.sourceName, "");
|
solAssert(_location.sourceName, "");
|
||||||
_context.markSourceUsed(*_location.sourceName);
|
_context.markSourceUsed(*_location.sourceName);
|
||||||
return "/// " + AsmPrinter::formatSourceLocation(
|
|
||||||
|
string debugInfo = AsmPrinter::formatSourceLocation(
|
||||||
_location,
|
_location,
|
||||||
_context.sourceIndices(),
|
_context.sourceIndices(),
|
||||||
|
_context.debugInfoSelection(),
|
||||||
_context.soliditySourceProvider()
|
_context.soliditySourceProvider()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return debugInfo.empty() ? "" : "/// " + debugInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
string dispenseLocationComment(ASTNode const& _node, IRGenerationContext& _context)
|
string dispenseLocationComment(ASTNode const& _node, IRGenerationContext& _context)
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <libsolidity/codegen/ir/Common.h>
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
|
|
||||||
#include <liblangutil/CharStreamProvider.h>
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
@ -74,6 +75,7 @@ public:
|
|||||||
RevertStrings _revertStrings,
|
RevertStrings _revertStrings,
|
||||||
OptimiserSettings _optimiserSettings,
|
OptimiserSettings _optimiserSettings,
|
||||||
std::map<std::string, unsigned> _sourceIndices,
|
std::map<std::string, unsigned> _sourceIndices,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection,
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider
|
langutil::CharStreamProvider const* _soliditySourceProvider
|
||||||
):
|
):
|
||||||
m_evmVersion(_evmVersion),
|
m_evmVersion(_evmVersion),
|
||||||
@ -81,6 +83,7 @@ public:
|
|||||||
m_revertStrings(_revertStrings),
|
m_revertStrings(_revertStrings),
|
||||||
m_optimiserSettings(std::move(_optimiserSettings)),
|
m_optimiserSettings(std::move(_optimiserSettings)),
|
||||||
m_sourceIndices(std::move(_sourceIndices)),
|
m_sourceIndices(std::move(_sourceIndices)),
|
||||||
|
m_debugInfoSelection(_debugInfoSelection),
|
||||||
m_soliditySourceProvider(_soliditySourceProvider)
|
m_soliditySourceProvider(_soliditySourceProvider)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -174,6 +177,7 @@ public:
|
|||||||
|
|
||||||
bool immutableRegistered(VariableDeclaration const& _varDecl) const { return m_immutableVariables.count(&_varDecl); }
|
bool immutableRegistered(VariableDeclaration const& _varDecl) const { return m_immutableVariables.count(&_varDecl); }
|
||||||
|
|
||||||
|
langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; }
|
||||||
langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; }
|
langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -220,6 +224,7 @@ private:
|
|||||||
|
|
||||||
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
||||||
|
|
||||||
|
langutil::DebugInfoSelection m_debugInfoSelection = {};
|
||||||
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +95,12 @@ pair<string, string> IRGenerator::run(
|
|||||||
{
|
{
|
||||||
string const ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
|
string const ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
|
||||||
|
|
||||||
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
yul::AssemblyStack asmStack(
|
||||||
|
m_evmVersion,
|
||||||
|
yul::AssemblyStack::Language::StrictAssembly,
|
||||||
|
m_optimiserSettings,
|
||||||
|
m_context.debugInfoSelection()
|
||||||
|
);
|
||||||
if (!asmStack.parseAndAnalyze("", ir))
|
if (!asmStack.parseAndAnalyze("", ir))
|
||||||
{
|
{
|
||||||
string errorMessage;
|
string errorMessage;
|
||||||
@ -340,8 +345,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
m_context.resetLocalVariables();
|
m_context.resetLocalVariables();
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
/// @ast-id <astID>
|
<astIDComment><sourceLocationComment>
|
||||||
<sourceLocationComment>
|
|
||||||
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||||
<retInit>
|
<retInit>
|
||||||
<body>
|
<body>
|
||||||
@ -349,7 +353,10 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
<contractSourceLocationComment>
|
<contractSourceLocationComment>
|
||||||
)");
|
)");
|
||||||
|
|
||||||
t("astID", to_string(_function.id()));
|
if (m_context.debugInfoSelection().astID)
|
||||||
|
t("astIDComment", "/// @ast-id " + to_string(_function.id()) + "\n");
|
||||||
|
else
|
||||||
|
t("astIDComment", "");
|
||||||
t("sourceLocationComment", dispenseLocationComment(_function));
|
t("sourceLocationComment", dispenseLocationComment(_function));
|
||||||
t(
|
t(
|
||||||
"contractSourceLocationComment",
|
"contractSourceLocationComment",
|
||||||
@ -409,8 +416,7 @@ string IRGenerator::generateModifier(
|
|||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
m_context.resetLocalVariables();
|
m_context.resetLocalVariables();
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
/// @ast-id <astID>
|
<astIDComment><sourceLocationComment>
|
||||||
<sourceLocationComment>
|
|
||||||
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||||
<assignRetParams>
|
<assignRetParams>
|
||||||
<evalArgs>
|
<evalArgs>
|
||||||
@ -418,6 +424,7 @@ string IRGenerator::generateModifier(
|
|||||||
}
|
}
|
||||||
<contractSourceLocationComment>
|
<contractSourceLocationComment>
|
||||||
)");
|
)");
|
||||||
|
|
||||||
t("functionName", functionName);
|
t("functionName", functionName);
|
||||||
vector<string> retParamsIn;
|
vector<string> retParamsIn;
|
||||||
for (auto const& varDecl: _function.returnParameters())
|
for (auto const& varDecl: _function.returnParameters())
|
||||||
@ -440,7 +447,11 @@ string IRGenerator::generateModifier(
|
|||||||
_modifierInvocation.name().annotation().referencedDeclaration
|
_modifierInvocation.name().annotation().referencedDeclaration
|
||||||
);
|
);
|
||||||
solAssert(modifier, "");
|
solAssert(modifier, "");
|
||||||
t("astID", to_string(modifier->id()));
|
|
||||||
|
if (m_context.debugInfoSelection().astID)
|
||||||
|
t("astIDComment", "/// @ast-id " + to_string(modifier->id()) + "\n");
|
||||||
|
else
|
||||||
|
t("astIDComment", "");
|
||||||
t("sourceLocationComment", dispenseLocationComment(*modifier));
|
t("sourceLocationComment", dispenseLocationComment(*modifier));
|
||||||
t(
|
t(
|
||||||
"contractSourceLocationComment",
|
"contractSourceLocationComment",
|
||||||
@ -546,14 +557,18 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
solAssert(paramTypes.empty(), "");
|
solAssert(paramTypes.empty(), "");
|
||||||
solUnimplementedAssert(type->sizeOnStack() == 1);
|
solUnimplementedAssert(type->sizeOnStack() == 1);
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
/// @ast-id <astID>
|
<astIDComment><sourceLocationComment>
|
||||||
<sourceLocationComment>
|
|
||||||
function <functionName>() -> rval {
|
function <functionName>() -> rval {
|
||||||
rval := loadimmutable("<id>")
|
rval := loadimmutable("<id>")
|
||||||
}
|
}
|
||||||
<contractSourceLocationComment>
|
<contractSourceLocationComment>
|
||||||
)")
|
)")
|
||||||
("astID", to_string(_varDecl.id()))
|
(
|
||||||
|
"astIDComment",
|
||||||
|
m_context.debugInfoSelection().astID ?
|
||||||
|
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
|
||||||
|
""
|
||||||
|
)
|
||||||
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
||||||
(
|
(
|
||||||
"contractSourceLocationComment",
|
"contractSourceLocationComment",
|
||||||
@ -567,14 +582,18 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
{
|
{
|
||||||
solAssert(paramTypes.empty(), "");
|
solAssert(paramTypes.empty(), "");
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
/// @ast-id <astID>
|
<astIDComment><sourceLocationComment>
|
||||||
<sourceLocationComment>
|
|
||||||
function <functionName>() -> <ret> {
|
function <functionName>() -> <ret> {
|
||||||
<ret> := <constantValueFunction>()
|
<ret> := <constantValueFunction>()
|
||||||
}
|
}
|
||||||
<contractSourceLocationComment>
|
<contractSourceLocationComment>
|
||||||
)")
|
)")
|
||||||
("astID", to_string(_varDecl.id()))
|
(
|
||||||
|
"astIDComment",
|
||||||
|
m_context.debugInfoSelection().astID ?
|
||||||
|
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
|
||||||
|
""
|
||||||
|
)
|
||||||
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
||||||
(
|
(
|
||||||
"contractSourceLocationComment",
|
"contractSourceLocationComment",
|
||||||
@ -691,8 +710,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
/// @ast-id <astID>
|
<astIDComment><sourceLocationComment>
|
||||||
<sourceLocationComment>
|
|
||||||
function <functionName>(<params>) -> <retVariables> {
|
function <functionName>(<params>) -> <retVariables> {
|
||||||
<code>
|
<code>
|
||||||
}
|
}
|
||||||
@ -702,7 +720,12 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
("params", joinHumanReadable(parameters))
|
("params", joinHumanReadable(parameters))
|
||||||
("retVariables", joinHumanReadable(returnVariables))
|
("retVariables", joinHumanReadable(returnVariables))
|
||||||
("code", std::move(code))
|
("code", std::move(code))
|
||||||
("astID", to_string(_varDecl.id()))
|
(
|
||||||
|
"astIDComment",
|
||||||
|
m_context.debugInfoSelection().astID ?
|
||||||
|
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
|
||||||
|
""
|
||||||
|
)
|
||||||
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
("sourceLocationComment", dispenseLocationComment(_varDecl))
|
||||||
(
|
(
|
||||||
"contractSourceLocationComment",
|
"contractSourceLocationComment",
|
||||||
@ -829,7 +852,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract)
|
|||||||
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
||||||
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||||
|
|
||||||
if (contract->constructor())
|
if (m_context.debugInfoSelection().astID && contract->constructor())
|
||||||
t("astIDComment", "/// @ast-id " + to_string(contract->constructor()->id()) + "\n");
|
t("astIDComment", "/// @ast-id " + to_string(contract->constructor()->id()) + "\n");
|
||||||
else
|
else
|
||||||
t("astIDComment", "");
|
t("astIDComment", "");
|
||||||
@ -944,7 +967,7 @@ string IRGenerator::callValueCheck()
|
|||||||
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
Whiskers t(R"X(
|
Whiskers t(R"X(
|
||||||
if iszero(lt(calldatasize(), 4))
|
<?+cases>if iszero(lt(calldatasize(), 4))
|
||||||
{
|
{
|
||||||
let selector := <shr224>(calldataload(0))
|
let selector := <shr224>(calldataload(0))
|
||||||
switch selector
|
switch selector
|
||||||
@ -962,8 +985,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
}
|
}
|
||||||
</cases>
|
</cases>
|
||||||
default {}
|
default {}
|
||||||
}
|
}</+cases>
|
||||||
if iszero(calldatasize()) { <receiveEther> }
|
<?+receiveEther>if iszero(calldatasize()) { <receiveEther> }</+receiveEther>
|
||||||
<fallback>
|
<fallback>
|
||||||
)X");
|
)X");
|
||||||
t("shr224", m_utils.shiftRightFunction(224));
|
t("shr224", m_utils.shiftRightFunction(224));
|
||||||
@ -1085,6 +1108,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon
|
|||||||
m_context.revertStrings(),
|
m_context.revertStrings(),
|
||||||
m_optimiserSettings,
|
m_optimiserSettings,
|
||||||
m_context.sourceIndices(),
|
m_context.sourceIndices(),
|
||||||
|
m_context.debugInfoSelection(),
|
||||||
m_context.soliditySourceProvider()
|
m_context.soliditySourceProvider()
|
||||||
);
|
);
|
||||||
newContext.copyFunctionIDsFrom(m_context);
|
newContext.copyFunctionIDsFrom(m_context);
|
||||||
|
@ -49,6 +49,7 @@ public:
|
|||||||
RevertStrings _revertStrings,
|
RevertStrings _revertStrings,
|
||||||
OptimiserSettings _optimiserSettings,
|
OptimiserSettings _optimiserSettings,
|
||||||
std::map<std::string, unsigned> _sourceIndices,
|
std::map<std::string, unsigned> _sourceIndices,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection,
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider
|
langutil::CharStreamProvider const* _soliditySourceProvider
|
||||||
):
|
):
|
||||||
m_evmVersion(_evmVersion),
|
m_evmVersion(_evmVersion),
|
||||||
@ -59,6 +60,7 @@ public:
|
|||||||
_revertStrings,
|
_revertStrings,
|
||||||
std::move(_optimiserSettings),
|
std::move(_optimiserSettings),
|
||||||
std::move(_sourceIndices),
|
std::move(_sourceIndices),
|
||||||
|
_debugInfoSelection,
|
||||||
_soliditySourceProvider
|
_soliditySourceProvider
|
||||||
),
|
),
|
||||||
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector())
|
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector())
|
||||||
|
@ -991,7 +991,7 @@ void CHC::resetSourceAnalysis()
|
|||||||
if (usesZ3)
|
if (usesZ3)
|
||||||
{
|
{
|
||||||
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
|
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
|
||||||
m_interface.reset(new Z3CHCInterface(m_settings.timeout));
|
m_interface = std::make_unique<Z3CHCInterface>(m_settings.timeout);
|
||||||
auto z3Interface = dynamic_cast<Z3CHCInterface const*>(m_interface.get());
|
auto z3Interface = dynamic_cast<Z3CHCInterface const*>(m_interface.get());
|
||||||
solAssert(z3Interface, "");
|
solAssert(z3Interface, "");
|
||||||
m_context.setSolver(z3Interface->z3Interface());
|
m_context.setSolver(z3Interface->z3Interface());
|
||||||
|
@ -201,7 +201,7 @@ void CompilerStack::findAndReportCyclicContractDependencies()
|
|||||||
void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings)
|
void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing."));
|
solThrow(CompilerError, "Must set remappings before parsing.");
|
||||||
for (auto const& remapping: _remappings)
|
for (auto const& remapping: _remappings)
|
||||||
solAssert(!remapping.prefix.empty(), "");
|
solAssert(!remapping.prefix.empty(), "");
|
||||||
m_importRemapper.setRemappings(move(_remappings));
|
m_importRemapper.setRemappings(move(_remappings));
|
||||||
@ -210,28 +210,28 @@ void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings)
|
|||||||
void CompilerStack::setViaIR(bool _viaIR)
|
void CompilerStack::setViaIR(bool _viaIR)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set viaIR before parsing."));
|
solThrow(CompilerError, "Must set viaIR before parsing.");
|
||||||
m_viaIR = _viaIR;
|
m_viaIR = _viaIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
|
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing."));
|
solThrow(CompilerError, "Must set EVM version before parsing.");
|
||||||
m_evmVersion = _version;
|
m_evmVersion = _version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
|
void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set model checking settings before parsing."));
|
solThrow(CompilerError, "Must set model checking settings before parsing.");
|
||||||
m_modelCheckerSettings = _settings;
|
m_modelCheckerSettings = _settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
|
void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing."));
|
solThrow(CompilerError, "Must set libraries before parsing.");
|
||||||
m_libraries = _libraries;
|
m_libraries = _libraries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,14 +245,14 @@ void CompilerStack::setOptimiserSettings(bool _optimize, size_t _runs)
|
|||||||
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
|
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
|
solThrow(CompilerError, "Must set optimiser settings before parsing.");
|
||||||
m_optimiserSettings = std::move(_settings);
|
m_optimiserSettings = std::move(_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
|
void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing."));
|
solThrow(CompilerError, "Must set revert string settings before parsing.");
|
||||||
solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug);
|
solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug);
|
||||||
m_revertStrings = _revertStrings;
|
m_revertStrings = _revertStrings;
|
||||||
}
|
}
|
||||||
@ -260,21 +260,28 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
|
|||||||
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
|
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing."));
|
solThrow(CompilerError, "Must set use literal sources before parsing.");
|
||||||
m_metadataLiteralSources = _metadataLiteralSources;
|
m_metadataLiteralSources = _metadataLiteralSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setMetadataHash(MetadataHash _metadataHash)
|
void CompilerStack::setMetadataHash(MetadataHash _metadataHash)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set metadata hash before parsing."));
|
solThrow(CompilerError, "Must set metadata hash before parsing.");
|
||||||
m_metadataHash = _metadataHash;
|
m_metadataHash = _metadataHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerStack::selectDebugInfo(DebugInfoSelection _debugInfoSelection)
|
||||||
|
{
|
||||||
|
if (m_stackState >= CompilationSuccessful)
|
||||||
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must select debug info components before compilation."));
|
||||||
|
m_debugInfoSelection = _debugInfoSelection;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response)
|
void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response)
|
||||||
{
|
{
|
||||||
if (m_stackState >= ParsedAndImported)
|
if (m_stackState >= ParsedAndImported)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing."));
|
solThrow(CompilerError, "Must add SMTLib2 responses before parsing.");
|
||||||
m_smtlib2Responses[_hash] = _response;
|
m_smtlib2Responses[_hash] = _response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,9 +317,9 @@ void CompilerStack::reset(bool _keepSettings)
|
|||||||
void CompilerStack::setSources(StringMap _sources)
|
void CompilerStack::setSources(StringMap _sources)
|
||||||
{
|
{
|
||||||
if (m_stackState == SourcesSet)
|
if (m_stackState == SourcesSet)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Cannot change sources once set."));
|
solThrow(CompilerError, "Cannot change sources once set.");
|
||||||
if (m_stackState != Empty)
|
if (m_stackState != Empty)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set sources before parsing."));
|
solThrow(CompilerError, "Must set sources before parsing.");
|
||||||
for (auto source: _sources)
|
for (auto source: _sources)
|
||||||
m_sources[source.first].charStream = make_unique<CharStream>(/*content*/std::move(source.second), /*name*/source.first);
|
m_sources[source.first].charStream = make_unique<CharStream>(/*content*/std::move(source.second), /*name*/source.first);
|
||||||
m_stackState = SourcesSet;
|
m_stackState = SourcesSet;
|
||||||
@ -321,7 +328,7 @@ void CompilerStack::setSources(StringMap _sources)
|
|||||||
bool CompilerStack::parse()
|
bool CompilerStack::parse()
|
||||||
{
|
{
|
||||||
if (m_stackState != SourcesSet)
|
if (m_stackState != SourcesSet)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call parse only after the SourcesSet state."));
|
solThrow(CompilerError, "Must call parse only after the SourcesSet state.");
|
||||||
m_errorReporter.clear();
|
m_errorReporter.clear();
|
||||||
|
|
||||||
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
||||||
@ -369,7 +376,7 @@ bool CompilerStack::parse()
|
|||||||
void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
|
void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
|
||||||
{
|
{
|
||||||
if (m_stackState != Empty)
|
if (m_stackState != Empty)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call importASTs only before the SourcesSet state."));
|
solThrow(CompilerError, "Must call importASTs only before the SourcesSet state.");
|
||||||
m_sourceJsons = _sources;
|
m_sourceJsons = _sources;
|
||||||
map<string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(m_sourceJsons);
|
map<string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(m_sourceJsons);
|
||||||
for (auto& src: reconstructedSources)
|
for (auto& src: reconstructedSources)
|
||||||
@ -392,7 +399,7 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
|
|||||||
bool CompilerStack::analyze()
|
bool CompilerStack::analyze()
|
||||||
{
|
{
|
||||||
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
|
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed."));
|
solThrow(CompilerError, "Must call analyze only after parsing was performed.");
|
||||||
resolveImports();
|
resolveImports();
|
||||||
|
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
@ -619,7 +626,7 @@ bool CompilerStack::compile(State _stopAfter)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
solThrow(CompilerError, "Called compile with errors.");
|
||||||
|
|
||||||
// Only compile contracts individually which have been requested.
|
// Only compile contracts individually which have been requested.
|
||||||
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||||
@ -691,7 +698,7 @@ void CompilerStack::link()
|
|||||||
vector<string> CompilerStack::contractNames() const
|
vector<string> CompilerStack::contractNames() const
|
||||||
{
|
{
|
||||||
if (m_stackState < Parsed)
|
if (m_stackState < Parsed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
solThrow(CompilerError, "Parsing was not successful.");
|
||||||
vector<string> contractNames;
|
vector<string> contractNames;
|
||||||
for (auto const& contract: m_contracts)
|
for (auto const& contract: m_contracts)
|
||||||
contractNames.push_back(contract.first);
|
contractNames.push_back(contract.first);
|
||||||
@ -701,7 +708,7 @@ vector<string> CompilerStack::contractNames() const
|
|||||||
string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
|
string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
solThrow(CompilerError, "Parsing was not successful.");
|
||||||
// try to find some user-supplied contract
|
// try to find some user-supplied contract
|
||||||
string contractName;
|
string contractName;
|
||||||
for (auto const& it: m_sources)
|
for (auto const& it: m_sources)
|
||||||
@ -714,7 +721,7 @@ string const CompilerStack::lastContractName(optional<string> const& _sourceName
|
|||||||
evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
return currentContract.evmAssembly ? ¤tContract.evmAssembly->items() : nullptr;
|
return currentContract.evmAssembly ? ¤tContract.evmAssembly->items() : nullptr;
|
||||||
@ -723,7 +730,7 @@ evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contrac
|
|||||||
evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
|
evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
return currentContract.evmRuntimeAssembly ? ¤tContract.evmRuntimeAssembly->items() : nullptr;
|
return currentContract.evmRuntimeAssembly ? ¤tContract.evmRuntimeAssembly->items() : nullptr;
|
||||||
@ -732,7 +739,7 @@ evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _
|
|||||||
Json::Value CompilerStack::generatedSources(string const& _contractName, bool _runtime) const
|
Json::Value CompilerStack::generatedSources(string const& _contractName, bool _runtime) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& c = contract(_contractName);
|
Contract const& c = contract(_contractName);
|
||||||
util::LazyInit<Json::Value const> const& sources =
|
util::LazyInit<Json::Value const> const& sources =
|
||||||
@ -775,7 +782,7 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r
|
|||||||
string const* CompilerStack::sourceMapping(string const& _contractName) const
|
string const* CompilerStack::sourceMapping(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& c = contract(_contractName);
|
Contract const& c = contract(_contractName);
|
||||||
if (!c.sourceMapping)
|
if (!c.sourceMapping)
|
||||||
@ -789,7 +796,7 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
|
|||||||
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
|
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& c = contract(_contractName);
|
Contract const& c = contract(_contractName);
|
||||||
if (!c.runtimeSourceMapping)
|
if (!c.runtimeSourceMapping)
|
||||||
@ -805,7 +812,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
|
|||||||
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
|
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
|
solThrow(CompilerError, "No compiled contracts found.");
|
||||||
|
|
||||||
// Look up the contract (by its fully-qualified name)
|
// Look up the contract (by its fully-qualified name)
|
||||||
Contract const& matchContract = m_contracts.at(_contractName);
|
Contract const& matchContract = m_contracts.at(_contractName);
|
||||||
@ -829,7 +836,7 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
|
|||||||
string const& CompilerStack::yulIR(string const& _contractName) const
|
string const& CompilerStack::yulIR(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).yulIR;
|
return contract(_contractName).yulIR;
|
||||||
}
|
}
|
||||||
@ -837,7 +844,7 @@ string const& CompilerStack::yulIR(string const& _contractName) const
|
|||||||
string const& CompilerStack::yulIROptimized(string const& _contractName) const
|
string const& CompilerStack::yulIROptimized(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).yulIROptimized;
|
return contract(_contractName).yulIROptimized;
|
||||||
}
|
}
|
||||||
@ -845,7 +852,7 @@ string const& CompilerStack::yulIROptimized(string const& _contractName) const
|
|||||||
string const& CompilerStack::ewasm(string const& _contractName) const
|
string const& CompilerStack::ewasm(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).ewasm;
|
return contract(_contractName).ewasm;
|
||||||
}
|
}
|
||||||
@ -853,7 +860,7 @@ string const& CompilerStack::ewasm(string const& _contractName) const
|
|||||||
evmasm::LinkerObject const& CompilerStack::ewasmObject(string const& _contractName) const
|
evmasm::LinkerObject const& CompilerStack::ewasmObject(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).ewasmObject;
|
return contract(_contractName).ewasmObject;
|
||||||
}
|
}
|
||||||
@ -861,7 +868,7 @@ evmasm::LinkerObject const& CompilerStack::ewasmObject(string const& _contractNa
|
|||||||
evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).object;
|
return contract(_contractName).object;
|
||||||
}
|
}
|
||||||
@ -869,7 +876,7 @@ evmasm::LinkerObject const& CompilerStack::object(string const& _contractName) c
|
|||||||
evmasm::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
|
evmasm::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
return contract(_contractName).runtimeObject;
|
return contract(_contractName).runtimeObject;
|
||||||
}
|
}
|
||||||
@ -878,11 +885,11 @@ evmasm::LinkerObject const& CompilerStack::runtimeObject(string const& _contract
|
|||||||
string CompilerStack::assemblyString(string const& _contractName, StringMap const& _sourceCodes) const
|
string CompilerStack::assemblyString(string const& _contractName, StringMap const& _sourceCodes) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
if (currentContract.evmAssembly)
|
if (currentContract.evmAssembly)
|
||||||
return currentContract.evmAssembly->assemblyString(_sourceCodes);
|
return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes);
|
||||||
else
|
else
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
@ -891,7 +898,7 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap cons
|
|||||||
Json::Value CompilerStack::assemblyJSON(string const& _contractName) const
|
Json::Value CompilerStack::assemblyJSON(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
if (currentContract.evmAssembly)
|
if (currentContract.evmAssembly)
|
||||||
@ -922,7 +929,7 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
|||||||
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return contractABI(contract(_contractName));
|
return contractABI(contract(_contractName));
|
||||||
}
|
}
|
||||||
@ -930,7 +937,7 @@ Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
|||||||
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
@ -940,7 +947,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
|||||||
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return storageLayout(contract(_contractName));
|
return storageLayout(contract(_contractName));
|
||||||
}
|
}
|
||||||
@ -948,7 +955,7 @@ Json::Value const& CompilerStack::storageLayout(string const& _contractName) con
|
|||||||
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
@ -958,7 +965,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
|
|||||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return natspecUser(contract(_contractName));
|
return natspecUser(contract(_contractName));
|
||||||
}
|
}
|
||||||
@ -966,7 +973,7 @@ Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
|||||||
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
@ -976,7 +983,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
|||||||
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return natspecDev(contract(_contractName));
|
return natspecDev(contract(_contractName));
|
||||||
}
|
}
|
||||||
@ -984,7 +991,7 @@ Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
|||||||
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
@ -994,7 +1001,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
|||||||
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
Json::Value methodIdentifiers(Json::objectValue);
|
Json::Value methodIdentifiers(Json::objectValue);
|
||||||
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
|
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
|
||||||
@ -1005,7 +1012,7 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
|||||||
bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const
|
bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return createCBORMetadata(contract(_contractName), _forIR);
|
return createCBORMetadata(contract(_contractName), _forIR);
|
||||||
}
|
}
|
||||||
@ -1013,7 +1020,7 @@ bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) cons
|
|||||||
string const& CompilerStack::metadata(Contract const& _contract) const
|
string const& CompilerStack::metadata(Contract const& _contract) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
|
|
||||||
@ -1023,7 +1030,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const
|
|||||||
CharStream const& CompilerStack::charStream(string const& _sourceName) const
|
CharStream const& CompilerStack::charStream(string const& _sourceName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < SourcesSet)
|
if (m_stackState < SourcesSet)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set."));
|
solThrow(CompilerError, "No sources set.");
|
||||||
|
|
||||||
solAssert(source(_sourceName).charStream, "");
|
solAssert(source(_sourceName).charStream, "");
|
||||||
|
|
||||||
@ -1033,9 +1040,9 @@ CharStream const& CompilerStack::charStream(string const& _sourceName) const
|
|||||||
SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < Parsed)
|
if (m_stackState < Parsed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing not yet performed."));
|
solThrow(CompilerError, "Parsing not yet performed.");
|
||||||
if (!source(_sourceName).ast && !m_parserErrorRecovery)
|
if (!source(_sourceName).ast && !m_parserErrorRecovery)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
solThrow(CompilerError, "Parsing was not successful.");
|
||||||
|
|
||||||
return *source(_sourceName).ast;
|
return *source(_sourceName).ast;
|
||||||
}
|
}
|
||||||
@ -1043,7 +1050,7 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
|||||||
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
|
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisPerformed)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
solThrow(CompilerError, "Analysis was not successful.");
|
||||||
|
|
||||||
return *contract(_contractName).contract;
|
return *contract(_contractName).contract;
|
||||||
}
|
}
|
||||||
@ -1054,7 +1061,7 @@ size_t CompilerStack::functionEntryPoint(
|
|||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
for (auto&& [name, data]: contract(_contractName).runtimeObject.functionDebugData)
|
for (auto&& [name, data]: contract(_contractName).runtimeObject.functionDebugData)
|
||||||
if (data.sourceID == _function.id())
|
if (data.sourceID == _function.id())
|
||||||
@ -1256,7 +1263,7 @@ void CompilerStack::compileContract(
|
|||||||
solAssert(!m_viaIR, "");
|
solAssert(!m_viaIR, "");
|
||||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
solThrow(CompilerError, "Called compile with errors.");
|
||||||
|
|
||||||
if (_otherCompilers.count(&_contract))
|
if (_otherCompilers.count(&_contract))
|
||||||
return;
|
return;
|
||||||
@ -1294,7 +1301,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateIR with errors."));
|
solThrow(CompilerError, "Called generateIR with errors.");
|
||||||
|
|
||||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
if (!compiledContract.yulIR.empty())
|
if (!compiledContract.yulIR.empty())
|
||||||
@ -1319,7 +1326,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
|||||||
for (auto const& pair: m_contracts)
|
for (auto const& pair: m_contracts)
|
||||||
otherYulSources.emplace(pair.second.contract, pair.second.yulIR);
|
otherYulSources.emplace(pair.second.contract, pair.second.yulIR);
|
||||||
|
|
||||||
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), this);
|
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), m_debugInfoSelection, this);
|
||||||
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(
|
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(
|
||||||
_contract,
|
_contract,
|
||||||
createCBORMetadata(compiledContract, /* _forIR */ true),
|
createCBORMetadata(compiledContract, /* _forIR */ true),
|
||||||
@ -1331,7 +1338,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEVMFromIR with errors."));
|
solThrow(CompilerError, "Called generateEVMFromIR with errors.");
|
||||||
|
|
||||||
if (!_contract.canBeDeployed())
|
if (!_contract.canBeDeployed())
|
||||||
return;
|
return;
|
||||||
@ -1342,7 +1349,12 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Re-parse the Yul IR in EVM dialect
|
// Re-parse the Yul IR in EVM dialect
|
||||||
yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
yul::AssemblyStack stack(
|
||||||
|
m_evmVersion,
|
||||||
|
yul::AssemblyStack::Language::StrictAssembly,
|
||||||
|
m_optimiserSettings,
|
||||||
|
m_debugInfoSelection
|
||||||
|
);
|
||||||
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
|
|
||||||
@ -1358,7 +1370,7 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||||
if (m_hasError)
|
if (m_hasError)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEwasm with errors."));
|
solThrow(CompilerError, "Called generateEwasm with errors.");
|
||||||
|
|
||||||
if (!_contract.canBeDeployed())
|
if (!_contract.canBeDeployed())
|
||||||
return;
|
return;
|
||||||
@ -1369,7 +1381,12 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Re-parse the Yul IR in EVM dialect
|
// Re-parse the Yul IR in EVM dialect
|
||||||
yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
yul::AssemblyStack stack(
|
||||||
|
m_evmVersion,
|
||||||
|
yul::AssemblyStack::Language::StrictAssembly,
|
||||||
|
m_optimiserSettings,
|
||||||
|
m_debugInfoSelection
|
||||||
|
);
|
||||||
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
stack.parseAndAnalyze("", compiledContract.yulIROptimized);
|
||||||
|
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
@ -1412,14 +1429,14 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, both lookup methods failed.
|
// If we get here, both lookup methods failed.
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract \"" + _contractName + "\" not found."));
|
solThrow(CompilerError, "Contract \"" + _contractName + "\" not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const
|
CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const
|
||||||
{
|
{
|
||||||
auto it = m_sources.find(_sourceName);
|
auto it = m_sources.find(_sourceName);
|
||||||
if (it == m_sources.end())
|
if (it == m_sources.end())
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
|
solThrow(CompilerError, "Given source file not found.");
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
@ -1658,7 +1675,7 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
|
|||||||
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||||
{
|
{
|
||||||
if (m_stackState != CompilationSuccessful)
|
if (m_stackState != CompilationSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
|
solThrow(CompilerError, "Compilation was not successful.");
|
||||||
|
|
||||||
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
|
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
|
||||||
return Json::Value();
|
return Json::Value();
|
||||||
|
@ -35,10 +35,11 @@
|
|||||||
|
|
||||||
#include <libsmtutil/SolverInterface.h>
|
#include <libsmtutil/SolverInterface.h>
|
||||||
|
|
||||||
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <liblangutil/CharStreamProvider.h>
|
|
||||||
|
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
|
|
||||||
@ -203,6 +204,9 @@ public:
|
|||||||
/// @param _metadataHash can be IPFS, Bzzr1, None
|
/// @param _metadataHash can be IPFS, Bzzr1, None
|
||||||
void setMetadataHash(MetadataHash _metadataHash);
|
void setMetadataHash(MetadataHash _metadataHash);
|
||||||
|
|
||||||
|
/// Select components of debug info that should be included in comments in generated assembly.
|
||||||
|
void selectDebugInfo(langutil::DebugInfoSelection _debugInfoSelection);
|
||||||
|
|
||||||
/// Sets the sources. Must be set before parsing.
|
/// Sets the sources. Must be set before parsing.
|
||||||
void setSources(StringMap _sources);
|
void setSources(StringMap _sources);
|
||||||
|
|
||||||
@ -505,6 +509,7 @@ private:
|
|||||||
langutil::ErrorReporter m_errorReporter;
|
langutil::ErrorReporter m_errorReporter;
|
||||||
bool m_metadataLiteralSources = false;
|
bool m_metadataLiteralSources = false;
|
||||||
MetadataHash m_metadataHash = MetadataHash::IPFS;
|
MetadataHash m_metadataHash = MetadataHash::IPFS;
|
||||||
|
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
|
||||||
bool m_parserErrorRecovery = false;
|
bool m_parserErrorRecovery = false;
|
||||||
State m_stackState = Empty;
|
State m_stackState = Empty;
|
||||||
bool m_importedSources = false;
|
bool m_importedSources = false;
|
||||||
|
@ -104,10 +104,7 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_kind != ReadCallback::kindString(ReadCallback::Kind::ReadFile))
|
if (_kind != ReadCallback::kindString(ReadCallback::Kind::ReadFile))
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
|
solAssert(false, "ReadFile callback used as callback kind " + _kind);
|
||||||
"ReadFile callback used as callback kind " +
|
|
||||||
_kind
|
|
||||||
));
|
|
||||||
string strippedSourceUnitName = _sourceUnitName;
|
string strippedSourceUnitName = _sourceUnitName;
|
||||||
if (strippedSourceUnitName.find("file://") == 0)
|
if (strippedSourceUnitName.find("file://") == 0)
|
||||||
strippedSourceUnitName.erase(0, 7);
|
strippedSourceUnitName.erase(0, 7);
|
||||||
@ -171,7 +168,7 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return ReadCallback::Result{false, "Unknown exception in read callback."};
|
return ReadCallback::Result{false, "Unknown exception in read callback: " + boost::current_exception_diagnostic_information()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,20 +44,20 @@ struct OptimiserSettings
|
|||||||
static char constexpr DefaultYulOptimiserSteps[] =
|
static char constexpr DefaultYulOptimiserSteps[] =
|
||||||
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse
|
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse
|
||||||
"["
|
"["
|
||||||
"xarrscLM" // Turn into SSA and simplify
|
"xa[r]scLM" // Turn into SSA and simplify
|
||||||
"cCTUtTOntnfDIul" // Perform structural simplification
|
"cCTUtTOntnfDIul" // Perform structural simplification
|
||||||
"Lcul" // Simplify again
|
"Lcul" // Simplify again
|
||||||
"Vcul jj" // Reverse SSA
|
"Vcul [j]" // Reverse SSA
|
||||||
|
|
||||||
// should have good "compilability" property here.
|
// should have good "compilability" property here.
|
||||||
|
|
||||||
"Tpeul" // Run functional expression inliner
|
"Tpeul" // Run functional expression inliner
|
||||||
"xarulrul" // Prune a bit more in SSA
|
"xa[rul]" // Prune a bit more in SSA
|
||||||
"xarrcL" // Turn into SSA again and simplify
|
"xa[r]cL" // Turn into SSA again and simplify
|
||||||
"gvif" // Run full inliner
|
"gvif" // Run full inliner
|
||||||
"CTUcarrLsTFOtfDncarrIulc" // SSA plus simplify
|
"CTUca[r]LsTFOtfDnca[r]Iulc" // SSA plus simplify
|
||||||
"]"
|
"]"
|
||||||
"jmuljuljul VcTOcul jmul"; // Make source short and pretty
|
"jmul[jul] VcTOcul jmul"; // Make source short and pretty
|
||||||
|
|
||||||
/// No optimisations at all - not recommended.
|
/// No optimisations at all - not recommended.
|
||||||
static OptimiserSettings none()
|
static OptimiserSettings none()
|
||||||
|
@ -28,9 +28,13 @@
|
|||||||
#include <libyul/AssemblyStack.h>
|
#include <libyul/AssemblyStack.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <libyul/optimiser/Suite.h>
|
#include <libyul/optimiser/Suite.h>
|
||||||
#include <liblangutil/SourceReferenceFormatter.h>
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
#include <libsmtutil/Exceptions.h>
|
#include <libsmtutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
@ -793,7 +797,7 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
|
|||||||
|
|
||||||
if (settings.isMember("debug"))
|
if (settings.isMember("debug"))
|
||||||
{
|
{
|
||||||
if (auto result = checkKeys(settings["debug"], {"revertStrings"}, "settings.debug"))
|
if (auto result = checkKeys(settings["debug"], {"revertStrings", "debugInfo"}, "settings.debug"))
|
||||||
return *result;
|
return *result;
|
||||||
|
|
||||||
if (settings["debug"].isMember("revertStrings"))
|
if (settings["debug"].isMember("revertStrings"))
|
||||||
@ -810,6 +814,31 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
|
|||||||
);
|
);
|
||||||
ret.revertStrings = *revertStrings;
|
ret.revertStrings = *revertStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings["debug"].isMember("debugInfo"))
|
||||||
|
{
|
||||||
|
if (!settings["debug"]["debugInfo"].isArray())
|
||||||
|
return formatFatalError("JSONError", "settings.debug.debugInfo must be an array.");
|
||||||
|
|
||||||
|
vector<string> components;
|
||||||
|
for (Json::Value const& arrayValue: settings["debug"]["debugInfo"])
|
||||||
|
components.push_back(arrayValue.asString());
|
||||||
|
|
||||||
|
optional<DebugInfoSelection> debugInfoSelection = DebugInfoSelection::fromComponents(
|
||||||
|
components,
|
||||||
|
true /* _acceptWildcards */
|
||||||
|
);
|
||||||
|
if (!debugInfoSelection.has_value())
|
||||||
|
return formatFatalError("JSONError", "Invalid value in settings.debug.debugInfo.");
|
||||||
|
|
||||||
|
if (debugInfoSelection->snippet && !debugInfoSelection->location)
|
||||||
|
return formatFatalError(
|
||||||
|
"JSONError",
|
||||||
|
"To use 'snippet' with settings.debug.debugInfo you must select also 'location'."
|
||||||
|
);
|
||||||
|
|
||||||
|
ret.debugInfoSelection = debugInfoSelection.value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.isMember("remappings") && !settings["remappings"].isArray())
|
if (settings.isMember("remappings") && !settings["remappings"].isArray())
|
||||||
@ -1029,6 +1058,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
compilerStack.setRemappings(move(_inputsAndSettings.remappings));
|
compilerStack.setRemappings(move(_inputsAndSettings.remappings));
|
||||||
compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
|
compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
|
||||||
compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);
|
compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);
|
||||||
|
if (_inputsAndSettings.debugInfoSelection.has_value())
|
||||||
|
compilerStack.selectDebugInfo(_inputsAndSettings.debugInfoSelection.value());
|
||||||
compilerStack.setLibraries(_inputsAndSettings.libraries);
|
compilerStack.setLibraries(_inputsAndSettings.libraries);
|
||||||
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
|
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
|
||||||
compilerStack.setMetadataHash(_inputsAndSettings.metadataHash);
|
compilerStack.setMetadataHash(_inputsAndSettings.metadataHash);
|
||||||
@ -1151,13 +1182,13 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
"Exception during compilation: " + boost::diagnostic_information(_exception)
|
"Exception during compilation: " + boost::diagnostic_information(_exception)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (std::exception const& _e)
|
catch (std::exception const& _exception)
|
||||||
{
|
{
|
||||||
errors.append(formatError(
|
errors.append(formatError(
|
||||||
Error::Severity::Error,
|
Error::Severity::Error,
|
||||||
"Exception",
|
"Exception",
|
||||||
"general",
|
"general",
|
||||||
"Unknown exception during compilation" + (_e.what() ? ": " + string(_e.what()) : ".")
|
"Unknown exception during compilation: " + boost::diagnostic_information(_exception)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
@ -1166,7 +1197,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
Error::Severity::Error,
|
Error::Severity::Error,
|
||||||
"Exception",
|
"Exception",
|
||||||
"general",
|
"general",
|
||||||
"Unknown exception during compilation."
|
"Unknown exception during compilation: " + boost::current_exception_diagnostic_information()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1358,7 +1389,10 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
|||||||
AssemblyStack stack(
|
AssemblyStack stack(
|
||||||
_inputsAndSettings.evmVersion,
|
_inputsAndSettings.evmVersion,
|
||||||
AssemblyStack::Language::StrictAssembly,
|
AssemblyStack::Language::StrictAssembly,
|
||||||
_inputsAndSettings.optimiserSettings
|
_inputsAndSettings.optimiserSettings,
|
||||||
|
_inputsAndSettings.debugInfoSelection.has_value() ?
|
||||||
|
_inputsAndSettings.debugInfoSelection.value() :
|
||||||
|
DebugInfoSelection::Default()
|
||||||
);
|
);
|
||||||
string const& sourceName = _inputsAndSettings.sources.begin()->first;
|
string const& sourceName = _inputsAndSettings.sources.begin()->first;
|
||||||
string const& sourceContents = _inputsAndSettings.sources.begin()->second;
|
string const& sourceContents = _inputsAndSettings.sources.begin()->second;
|
||||||
@ -1481,7 +1515,7 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile");
|
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile: " + boost::current_exception_diagnostic_information());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
@ -78,6 +80,7 @@ private:
|
|||||||
std::vector<ImportRemapper::Remapping> remappings;
|
std::vector<ImportRemapper::Remapping> remappings;
|
||||||
RevertStrings revertStrings = RevertStrings::Default;
|
RevertStrings revertStrings = RevertStrings::Default;
|
||||||
OptimiserSettings optimiserSettings = OptimiserSettings::minimal();
|
OptimiserSettings optimiserSettings = OptimiserSettings::minimal();
|
||||||
|
std::optional<langutil::DebugInfoSelection> debugInfoSelection;
|
||||||
std::map<std::string, util::h160> libraries;
|
std::map<std::string, util::h160> libraries;
|
||||||
bool metadataLiteralSources = false;
|
bool metadataLiteralSources = false;
|
||||||
CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS;
|
CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS;
|
||||||
|
@ -61,14 +61,9 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri
|
|||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
if (!(_condition)) \
|
if (!(_condition)) \
|
||||||
::boost::throw_exception( \
|
solThrow( \
|
||||||
_exceptionType() << \
|
_exceptionType, \
|
||||||
::solidity::util::errinfo_comment( \
|
|
||||||
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
|
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
|
||||||
) << \
|
|
||||||
::boost::throw_function(ETH_FUNC) << \
|
|
||||||
::boost::throw_file(__FILE__) << \
|
|
||||||
::boost::throw_line(__LINE__) \
|
|
||||||
); \
|
); \
|
||||||
} \
|
} \
|
||||||
while (false)
|
while (false)
|
||||||
|
@ -43,6 +43,19 @@ struct Exception: virtual std::exception, virtual boost::exception
|
|||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Throws an exception with a given description and extra information about the location the
|
||||||
|
/// exception was thrown from.
|
||||||
|
/// @param _exceptionType The type of the exception to throw (not an instance).
|
||||||
|
/// @param _description The message that describes the error.
|
||||||
|
#define solThrow(_exceptionType, _description) \
|
||||||
|
::boost::throw_exception( \
|
||||||
|
_exceptionType() << \
|
||||||
|
::solidity::util::errinfo_comment(_description) << \
|
||||||
|
::boost::throw_function(ETH_FUNC) << \
|
||||||
|
::boost::throw_file(__FILE__) << \
|
||||||
|
::boost::throw_line(__LINE__) \
|
||||||
|
)
|
||||||
|
|
||||||
#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual ::solidity::util::Exception { const char* what() const noexcept override { return #X; } }
|
#define DEV_SIMPLE_EXCEPTION(X) struct X: virtual ::solidity::util::Exception { const char* what() const noexcept override { return #X; } }
|
||||||
|
|
||||||
DEV_SIMPLE_EXCEPTION(InvalidAddress);
|
DEV_SIMPLE_EXCEPTION(InvalidAddress);
|
||||||
|
@ -189,11 +189,13 @@ string Whiskers::replace(
|
|||||||
if (conditionName[0] == '+')
|
if (conditionName[0] == '+')
|
||||||
{
|
{
|
||||||
string tag = conditionName.substr(1);
|
string tag = conditionName.substr(1);
|
||||||
assertThrow(
|
|
||||||
_parameters.count(tag),
|
if (_parameters.count(tag))
|
||||||
WhiskersError, "Tag " + tag + " used as condition but was not set."
|
|
||||||
);
|
|
||||||
conditionValue = !_parameters.at(tag).empty();
|
conditionValue = !_parameters.at(tag).empty();
|
||||||
|
else if (_listParameters.count(tag))
|
||||||
|
conditionValue = !_listParameters.at(tag).empty();
|
||||||
|
else
|
||||||
|
assertThrow(false, WhiskersError, "Tag " + tag + " used as condition but was not set.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -60,12 +60,13 @@ DEV_SIMPLE_EXCEPTION(WhiskersError);
|
|||||||
* - Condition parameter: <?name>...<!name>...</name>, where "<!name>" is optional
|
* - Condition parameter: <?name>...<!name>...</name>, where "<!name>" is optional
|
||||||
* replaced (and recursively expanded) by the first part if the condition is true
|
* replaced (and recursively expanded) by the first part if the condition is true
|
||||||
* and by the second (or empty string if missing) if the condition is false
|
* and by the second (or empty string if missing) if the condition is false
|
||||||
* - Conditional string parameter: <?+name>...<!+name>...</+name>
|
|
||||||
* Works similar to a conditional parameter where the checked condition is
|
|
||||||
* that the regular (string) parameter called "name" is non-empty.
|
|
||||||
* - List parameter: <#list>...</list>
|
* - List parameter: <#list>...</list>
|
||||||
* The part between the tags is repeated as often as values are provided
|
* The part between the tags is repeated as often as values are provided
|
||||||
* in the mapping. Each list element can have its own parameter -> value mapping.
|
* in the mapping. Each list element can have its own parameter -> value mapping.
|
||||||
|
* - Conditional value parameter: <?+name>...<!+name>...</+name>
|
||||||
|
* Works similar to a conditional parameter where the checked condition is
|
||||||
|
* that the string or list parameter called "name" is non-empty or contains
|
||||||
|
* no elements respectively.
|
||||||
*/
|
*/
|
||||||
class Whiskers
|
class Whiskers
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@ -618,7 +620,7 @@ void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _locati
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
5473_error,
|
5473_error,
|
||||||
_location,
|
_location,
|
||||||
"\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)."
|
fmt::format("\"{}\" is not a valid type (user defined types are not yet supported).", _type)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,11 +630,7 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
3781_error,
|
3781_error,
|
||||||
_location,
|
_location,
|
||||||
"Expected a value of type \"" +
|
fmt::format("Expected a value of type \"{}\" but got \"{}\".", _expectedType, _givenType)
|
||||||
_expectedType.str() +
|
|
||||||
"\" but got \"" +
|
|
||||||
_givenType.str() +
|
|
||||||
"\""
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,14 +662,12 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
_errorId,
|
_errorId,
|
||||||
_location,
|
_location,
|
||||||
"The \"" +
|
fmt::format(
|
||||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
"The \"{instruction}\" instruction is {kind} VMs (you are currently compiling for \"{version}\").",
|
||||||
+ "\" instruction is " +
|
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr).name)),
|
||||||
vmKindMessage +
|
fmt::arg("kind", vmKindMessage),
|
||||||
" VMs " +
|
fmt::arg("version", m_evmVersion.name())
|
||||||
"(you are currently compiling for \"" +
|
)
|
||||||
m_evmVersion.name() +
|
|
||||||
"\")."
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -261,10 +261,16 @@ string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const
|
|||||||
string AsmPrinter::formatSourceLocation(
|
string AsmPrinter::formatSourceLocation(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
map<string, unsigned> const& _nameToSourceIndex,
|
map<string, unsigned> const& _nameToSourceIndex,
|
||||||
|
DebugInfoSelection const& _debugInfoSelection,
|
||||||
CharStreamProvider const* _soliditySourceProvider
|
CharStreamProvider const* _soliditySourceProvider
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
yulAssert(!_nameToSourceIndex.empty(), "");
|
yulAssert(!_nameToSourceIndex.empty(), "");
|
||||||
|
if (_debugInfoSelection.snippet)
|
||||||
|
yulAssert(_debugInfoSelection.location, "@src tag must always contain the source location");
|
||||||
|
|
||||||
|
if (_debugInfoSelection.none())
|
||||||
|
return "";
|
||||||
|
|
||||||
string sourceIndex = "-1";
|
string sourceIndex = "-1";
|
||||||
string solidityCodeSnippet = "";
|
string solidityCodeSnippet = "";
|
||||||
@ -272,7 +278,7 @@ string AsmPrinter::formatSourceLocation(
|
|||||||
{
|
{
|
||||||
sourceIndex = to_string(_nameToSourceIndex.at(*_location.sourceName));
|
sourceIndex = to_string(_nameToSourceIndex.at(*_location.sourceName));
|
||||||
|
|
||||||
if (_soliditySourceProvider)
|
if (_debugInfoSelection.snippet && _soliditySourceProvider)
|
||||||
{
|
{
|
||||||
solidityCodeSnippet = escapeAndQuoteString(
|
solidityCodeSnippet = escapeAndQuoteString(
|
||||||
_soliditySourceProvider->charStream(*_location.sourceName).singleLineSnippet(_location)
|
_soliditySourceProvider->charStream(*_location.sourceName).singleLineSnippet(_location)
|
||||||
@ -298,11 +304,12 @@ string AsmPrinter::formatSourceLocation(
|
|||||||
|
|
||||||
string AsmPrinter::formatDebugData(shared_ptr<DebugData const> const& _debugData, bool _statement)
|
string AsmPrinter::formatDebugData(shared_ptr<DebugData const> const& _debugData, bool _statement)
|
||||||
{
|
{
|
||||||
if (!_debugData)
|
if (!_debugData || m_debugInfoSelection.none())
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
vector<string> items;
|
vector<string> items;
|
||||||
if (auto id = _debugData->astID)
|
if (auto id = _debugData->astID)
|
||||||
|
if (m_debugInfoSelection.astID)
|
||||||
items.emplace_back("@ast-id " + to_string(*id));
|
items.emplace_back("@ast-id " + to_string(*id));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -315,6 +322,7 @@ string AsmPrinter::formatDebugData(shared_ptr<DebugData const> const& _debugData
|
|||||||
items.emplace_back(formatSourceLocation(
|
items.emplace_back(formatSourceLocation(
|
||||||
_debugData->originLocation,
|
_debugData->originLocation,
|
||||||
m_nameToSourceIndex,
|
m_nameToSourceIndex,
|
||||||
|
m_debugInfoSelection,
|
||||||
m_soliditySourceProvider
|
m_soliditySourceProvider
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <liblangutil/CharStreamProvider.h>
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -48,9 +49,11 @@ public:
|
|||||||
explicit AsmPrinter(
|
explicit AsmPrinter(
|
||||||
Dialect const* _dialect = nullptr,
|
Dialect const* _dialect = nullptr,
|
||||||
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
|
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
||||||
):
|
):
|
||||||
m_dialect(_dialect),
|
m_dialect(_dialect),
|
||||||
|
m_debugInfoSelection(_debugInfoSelection),
|
||||||
m_soliditySourceProvider(_soliditySourceProvider)
|
m_soliditySourceProvider(_soliditySourceProvider)
|
||||||
{
|
{
|
||||||
if (_sourceIndexToName)
|
if (_sourceIndexToName)
|
||||||
@ -58,12 +61,12 @@ public:
|
|||||||
m_nameToSourceIndex[*name] = index;
|
m_nameToSourceIndex[*name] = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
explicit AsmPrinter(
|
explicit AsmPrinter(
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
|
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
||||||
): AsmPrinter(&_dialect, _sourceIndexToName, _soliditySourceProvider) {}
|
): AsmPrinter(&_dialect, _sourceIndexToName, _debugInfoSelection, _soliditySourceProvider) {}
|
||||||
|
|
||||||
std::string operator()(Literal const& _literal);
|
std::string operator()(Literal const& _literal);
|
||||||
std::string operator()(Identifier const& _identifier);
|
std::string operator()(Identifier const& _identifier);
|
||||||
@ -83,6 +86,7 @@ public:
|
|||||||
static std::string formatSourceLocation(
|
static std::string formatSourceLocation(
|
||||||
langutil::SourceLocation const& _location,
|
langutil::SourceLocation const& _location,
|
||||||
std::map<std::string, unsigned> const& _nameToSourceIndex,
|
std::map<std::string, unsigned> const& _nameToSourceIndex,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr
|
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -100,6 +104,7 @@ private:
|
|||||||
Dialect const* const m_dialect = nullptr;
|
Dialect const* const m_dialect = nullptr;
|
||||||
std::map<std::string, unsigned> m_nameToSourceIndex;
|
std::map<std::string, unsigned> m_nameToSourceIndex;
|
||||||
langutil::SourceLocation m_lastLocation = {};
|
langutil::SourceLocation m_lastLocation = {};
|
||||||
|
langutil::DebugInfoSelection m_debugInfoSelection = {};
|
||||||
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
|||||||
MachineAssemblyObject creationObject;
|
MachineAssemblyObject creationObject;
|
||||||
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
|
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
|
||||||
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
|
||||||
creationObject.assembly = creationAssembly->assemblyString();
|
creationObject.assembly = creationAssembly->assemblyString(m_debugInfoSelection);
|
||||||
creationObject.sourceMappings = make_unique<string>(
|
creationObject.sourceMappings = make_unique<string>(
|
||||||
evmasm::AssemblyItem::computeSourceMapping(
|
evmasm::AssemblyItem::computeSourceMapping(
|
||||||
creationAssembly->items(),
|
creationAssembly->items(),
|
||||||
@ -261,7 +261,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
|
|||||||
if (deployedAssembly)
|
if (deployedAssembly)
|
||||||
{
|
{
|
||||||
deployedObject.bytecode = make_shared<evmasm::LinkerObject>(deployedAssembly->assemble());
|
deployedObject.bytecode = make_shared<evmasm::LinkerObject>(deployedAssembly->assemble());
|
||||||
deployedObject.assembly = deployedAssembly->assemblyString();
|
deployedObject.assembly = deployedAssembly->assemblyString(m_debugInfoSelection);
|
||||||
deployedObject.sourceMappings = make_unique<string>(
|
deployedObject.sourceMappings = make_unique<string>(
|
||||||
evmasm::AssemblyItem::computeSourceMapping(
|
evmasm::AssemblyItem::computeSourceMapping(
|
||||||
deployedAssembly->items(),
|
deployedAssembly->items(),
|
||||||
@ -314,11 +314,13 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
|
|||||||
return {make_shared<evmasm::Assembly>(assembly), {}};
|
return {make_shared<evmasm::Assembly>(assembly), {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
string AssemblyStack::print(CharStreamProvider const* _soliditySourceProvider) const
|
string AssemblyStack::print(
|
||||||
|
CharStreamProvider const* _soliditySourceProvider
|
||||||
|
) const
|
||||||
{
|
{
|
||||||
yulAssert(m_parserResult, "");
|
yulAssert(m_parserResult, "");
|
||||||
yulAssert(m_parserResult->code, "");
|
yulAssert(m_parserResult->code, "");
|
||||||
return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion), _soliditySourceProvider) + "\n";
|
return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion), m_debugInfoSelection, _soliditySourceProvider) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<Object> AssemblyStack::parserResult() const
|
shared_ptr<Object> AssemblyStack::parserResult() const
|
||||||
|
@ -22,9 +22,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
#include <liblangutil/CharStreamProvider.h>
|
|
||||||
|
|
||||||
#include <libyul/Object.h>
|
#include <libyul/Object.h>
|
||||||
#include <libyul/ObjectParser.h>
|
#include <libyul/ObjectParser.h>
|
||||||
@ -69,12 +70,24 @@ public:
|
|||||||
enum class Machine { EVM, Ewasm };
|
enum class Machine { EVM, Ewasm };
|
||||||
|
|
||||||
AssemblyStack():
|
AssemblyStack():
|
||||||
AssemblyStack(langutil::EVMVersion{}, Language::Assembly, solidity::frontend::OptimiserSettings::none())
|
AssemblyStack(
|
||||||
|
langutil::EVMVersion{},
|
||||||
|
Language::Assembly,
|
||||||
|
solidity::frontend::OptimiserSettings::none(),
|
||||||
|
langutil::DebugInfoSelection::Default()
|
||||||
|
)
|
||||||
{}
|
{}
|
||||||
AssemblyStack(langutil::EVMVersion _evmVersion, Language _language, solidity::frontend::OptimiserSettings _optimiserSettings):
|
|
||||||
|
AssemblyStack(
|
||||||
|
langutil::EVMVersion _evmVersion,
|
||||||
|
Language _language,
|
||||||
|
solidity::frontend::OptimiserSettings _optimiserSettings,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection
|
||||||
|
):
|
||||||
m_language(_language),
|
m_language(_language),
|
||||||
m_evmVersion(_evmVersion),
|
m_evmVersion(_evmVersion),
|
||||||
m_optimiserSettings(std::move(_optimiserSettings)),
|
m_optimiserSettings(std::move(_optimiserSettings)),
|
||||||
|
m_debugInfoSelection(_debugInfoSelection),
|
||||||
m_errorReporter(m_errors)
|
m_errorReporter(m_errors)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -116,7 +129,9 @@ public:
|
|||||||
langutil::ErrorList const& errors() const { return m_errors; }
|
langutil::ErrorList const& errors() const { return m_errors; }
|
||||||
|
|
||||||
/// Pretty-print the input after having parsed it.
|
/// Pretty-print the input after having parsed it.
|
||||||
std::string print(langutil::CharStreamProvider const* _soliditySourceProvider = nullptr) const;
|
std::string print(
|
||||||
|
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
||||||
|
) const;
|
||||||
|
|
||||||
/// Return the parsed and analyzed object.
|
/// Return the parsed and analyzed object.
|
||||||
std::shared_ptr<Object> parserResult() const;
|
std::shared_ptr<Object> parserResult() const;
|
||||||
@ -132,6 +147,7 @@ private:
|
|||||||
Language m_language = Language::Assembly;
|
Language m_language = Language::Assembly;
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
solidity::frontend::OptimiserSettings m_optimiserSettings;
|
solidity::frontend::OptimiserSettings m_optimiserSettings;
|
||||||
|
langutil::DebugInfoSelection m_debugInfoSelection{};
|
||||||
|
|
||||||
std::unique_ptr<langutil::CharStream> m_charStream;
|
std::unique_ptr<langutil::CharStream> m_charStream;
|
||||||
|
|
||||||
|
@ -34,6 +34,9 @@ add_library(yul
|
|||||||
AssemblyStack.cpp
|
AssemblyStack.cpp
|
||||||
CompilabilityChecker.cpp
|
CompilabilityChecker.cpp
|
||||||
CompilabilityChecker.h
|
CompilabilityChecker.h
|
||||||
|
ControlFlowSideEffects.h
|
||||||
|
ControlFlowSideEffectsCollector.cpp
|
||||||
|
ControlFlowSideEffectsCollector.h
|
||||||
Dialect.cpp
|
Dialect.cpp
|
||||||
Dialect.h
|
Dialect.h
|
||||||
Exceptions.h
|
Exceptions.h
|
||||||
@ -216,4 +219,4 @@ add_library(yul
|
|||||||
optimiser/VarNameCleaner.h
|
optimiser/VarNameCleaner.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil)
|
target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil fmt::fmt-header-only)
|
||||||
|
@ -18,22 +18,32 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Side effects of code related to control flow.
|
* Side effects of a user-defined or builtin function.
|
||||||
|
*
|
||||||
|
* Each of the three booleans represents a reachability condition. There is an implied
|
||||||
|
* fourth alternative, which is going out of gas while executing the function. Since
|
||||||
|
* this can always happen and depends on the supply of gas, it is not considered.
|
||||||
|
*
|
||||||
|
* If all three booleans are false, it means that the function always leads to infinite
|
||||||
|
* recursion.
|
||||||
*/
|
*/
|
||||||
struct ControlFlowSideEffects
|
struct ControlFlowSideEffects
|
||||||
{
|
{
|
||||||
/// If true, this code terminates the control flow.
|
/// If true, the function contains at least one reachable branch that terminates successfully.
|
||||||
/// State may or may not be reverted as indicated by the ``reverts`` flag.
|
bool canTerminate = false;
|
||||||
bool terminates = false;
|
/// If true, the function contains at least one reachable branch that reverts.
|
||||||
/// If true, this code reverts all state changes in the transaction.
|
bool canRevert = false;
|
||||||
/// Whenever this is true, ``terminates`` has to be true as well.
|
/// If true, the function has a regular outgoing control-flow.
|
||||||
bool reverts = false;
|
bool canContinue = true;
|
||||||
|
|
||||||
|
bool terminatesOrReverts() const
|
||||||
|
{
|
||||||
|
return (canTerminate || canRevert) && !canContinue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
274
libyul/ControlFlowSideEffectsCollector.cpp
Normal file
274
libyul/ControlFlowSideEffectsCollector.cpp
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||||
|
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Common.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/Algorithms.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/map.hpp>
|
||||||
|
#include <range/v3/view/reverse.hpp>
|
||||||
|
#include <range/v3/algorithm/find_if.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
|
||||||
|
ControlFlowBuilder::ControlFlowBuilder(Block const& _ast)
|
||||||
|
{
|
||||||
|
for (auto const& statement: _ast.statements)
|
||||||
|
if (auto const* function = get_if<FunctionDefinition>(&statement))
|
||||||
|
(*this)(*function);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
walkVector(_functionCall.arguments | ranges::views::reverse);
|
||||||
|
newConnectedNode();
|
||||||
|
m_currentNode->functionCall = _functionCall.functionName.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(If const& _if)
|
||||||
|
{
|
||||||
|
visit(*_if.condition);
|
||||||
|
ControlFlowNode* node = m_currentNode;
|
||||||
|
(*this)(_if.body);
|
||||||
|
newConnectedNode();
|
||||||
|
node->successors.emplace_back(m_currentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(Switch const& _switch)
|
||||||
|
{
|
||||||
|
visit(*_switch.expression);
|
||||||
|
ControlFlowNode* initialNode = m_currentNode;
|
||||||
|
ControlFlowNode* finalNode = newNode();
|
||||||
|
|
||||||
|
if (_switch.cases.back().value)
|
||||||
|
initialNode->successors.emplace_back(finalNode);
|
||||||
|
|
||||||
|
for (Case const& case_: _switch.cases)
|
||||||
|
{
|
||||||
|
m_currentNode = initialNode;
|
||||||
|
(*this)(case_.body);
|
||||||
|
newConnectedNode();
|
||||||
|
m_currentNode->successors.emplace_back(finalNode);
|
||||||
|
}
|
||||||
|
m_currentNode = finalNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
ScopedSaveAndRestore currentNode(m_currentNode, nullptr);
|
||||||
|
yulAssert(!m_leave && !m_break && !m_continue, "Function hoister has not been used.");
|
||||||
|
|
||||||
|
FunctionFlow flow;
|
||||||
|
flow.exit = newNode();
|
||||||
|
m_currentNode = newNode();
|
||||||
|
flow.entry = m_currentNode;
|
||||||
|
m_leave = flow.exit;
|
||||||
|
|
||||||
|
(*this)(_function.body);
|
||||||
|
|
||||||
|
m_currentNode->successors.emplace_back(flow.exit);
|
||||||
|
|
||||||
|
m_functionFlows[_function.name] = move(flow);
|
||||||
|
|
||||||
|
m_leave = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(ForLoop const& _for)
|
||||||
|
{
|
||||||
|
ScopedSaveAndRestore scopedBreakNode(m_break, nullptr);
|
||||||
|
ScopedSaveAndRestore scopedContinueNode(m_continue, nullptr);
|
||||||
|
|
||||||
|
(*this)(_for.pre);
|
||||||
|
|
||||||
|
ControlFlowNode* breakNode = newNode();
|
||||||
|
m_break = breakNode;
|
||||||
|
ControlFlowNode* continueNode = newNode();
|
||||||
|
m_continue = continueNode;
|
||||||
|
|
||||||
|
newConnectedNode();
|
||||||
|
ControlFlowNode* loopNode = m_currentNode;
|
||||||
|
visit(*_for.condition);
|
||||||
|
m_currentNode->successors.emplace_back(m_break);
|
||||||
|
newConnectedNode();
|
||||||
|
|
||||||
|
(*this)(_for.body);
|
||||||
|
|
||||||
|
m_currentNode->successors.emplace_back(m_continue);
|
||||||
|
m_currentNode = continueNode;
|
||||||
|
|
||||||
|
(*this)(_for.post);
|
||||||
|
m_currentNode->successors.emplace_back(loopNode);
|
||||||
|
|
||||||
|
m_currentNode = breakNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(Break const&)
|
||||||
|
{
|
||||||
|
yulAssert(m_break);
|
||||||
|
m_currentNode->successors.emplace_back(m_break);
|
||||||
|
m_currentNode = newNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(Continue const&)
|
||||||
|
{
|
||||||
|
yulAssert(m_continue);
|
||||||
|
m_currentNode->successors.emplace_back(m_continue);
|
||||||
|
m_currentNode = newNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::operator()(Leave const&)
|
||||||
|
{
|
||||||
|
yulAssert(m_leave);
|
||||||
|
m_currentNode->successors.emplace_back(m_leave);
|
||||||
|
m_currentNode = newNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowBuilder::newConnectedNode()
|
||||||
|
{
|
||||||
|
ControlFlowNode* node = newNode();
|
||||||
|
m_currentNode->successors.emplace_back(node);
|
||||||
|
m_currentNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlowNode* ControlFlowBuilder::newNode()
|
||||||
|
{
|
||||||
|
m_nodes.emplace_back(make_shared<ControlFlowNode>());
|
||||||
|
return m_nodes.back().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block const& _ast
|
||||||
|
):
|
||||||
|
m_dialect(_dialect),
|
||||||
|
m_cfgBuilder(_ast)
|
||||||
|
{
|
||||||
|
for (auto&& [name, flow]: m_cfgBuilder.functionFlows())
|
||||||
|
{
|
||||||
|
yulAssert(!flow.entry->functionCall);
|
||||||
|
m_processedNodes[name] = {};
|
||||||
|
m_pendingNodes[name].push_front(flow.entry);
|
||||||
|
m_functionSideEffects[name] = {false, false, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process functions while we have progress. For now, we are only interested
|
||||||
|
// in `canContinue`.
|
||||||
|
bool progress = true;
|
||||||
|
while (progress)
|
||||||
|
{
|
||||||
|
progress = false;
|
||||||
|
for (auto const& functionName: m_pendingNodes | ranges::views::keys)
|
||||||
|
if (processFunction(functionName))
|
||||||
|
progress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No progress anymore: All remaining nodes are calls
|
||||||
|
// to functions that always recurse.
|
||||||
|
// If we have not set `canContinue` by now, the function's exit
|
||||||
|
// is not reachable.
|
||||||
|
|
||||||
|
for (auto&& [functionName, calls]: m_functionCalls)
|
||||||
|
{
|
||||||
|
ControlFlowSideEffects& sideEffects = m_functionSideEffects[functionName];
|
||||||
|
auto _visit = [&, visited = std::set<YulString>{}](YulString _function, auto&& _recurse) mutable {
|
||||||
|
if (sideEffects.canTerminate && sideEffects.canRevert)
|
||||||
|
return;
|
||||||
|
if (!visited.insert(_function).second)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ControlFlowSideEffects const* calledSideEffects = nullptr;
|
||||||
|
if (BuiltinFunction const* f = _dialect.builtin(_function))
|
||||||
|
calledSideEffects = &f->controlFlowSideEffects;
|
||||||
|
else
|
||||||
|
calledSideEffects = &m_functionSideEffects.at(_function);
|
||||||
|
|
||||||
|
if (calledSideEffects->canTerminate)
|
||||||
|
sideEffects.canTerminate = true;
|
||||||
|
if (calledSideEffects->canRevert)
|
||||||
|
sideEffects.canRevert = true;
|
||||||
|
|
||||||
|
for (YulString callee: util::valueOrDefault(m_functionCalls, _function))
|
||||||
|
_recurse(callee, _recurse);
|
||||||
|
};
|
||||||
|
for (auto const& call: calls)
|
||||||
|
_visit(call, _visit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ControlFlowSideEffectsCollector::processFunction(YulString _name)
|
||||||
|
{
|
||||||
|
bool progress = false;
|
||||||
|
while (ControlFlowNode const* node = nextProcessableNode(_name))
|
||||||
|
{
|
||||||
|
if (node == m_cfgBuilder.functionFlows().at(_name).exit)
|
||||||
|
{
|
||||||
|
m_functionSideEffects[_name].canContinue = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (ControlFlowNode const* s: node->successors)
|
||||||
|
recordReachabilityAndQueue(_name, s);
|
||||||
|
|
||||||
|
progress = true;
|
||||||
|
}
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(YulString _functionName)
|
||||||
|
{
|
||||||
|
std::list<ControlFlowNode const*>& nodes = m_pendingNodes[_functionName];
|
||||||
|
auto it = ranges::find_if(nodes, [this](ControlFlowNode const* _node) {
|
||||||
|
return !_node->functionCall || sideEffects(*_node->functionCall).canContinue;
|
||||||
|
});
|
||||||
|
if (it == nodes.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
ControlFlowNode const* node = *it;
|
||||||
|
nodes.erase(it);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlowSideEffects const& ControlFlowSideEffectsCollector::sideEffects(YulString _functionName) const
|
||||||
|
{
|
||||||
|
if (auto const* builtin = m_dialect.builtin(_functionName))
|
||||||
|
return builtin->controlFlowSideEffects;
|
||||||
|
else
|
||||||
|
return m_functionSideEffects.at(_functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlFlowSideEffectsCollector::recordReachabilityAndQueue(
|
||||||
|
YulString _functionName,
|
||||||
|
ControlFlowNode const* _node
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_node->functionCall)
|
||||||
|
m_functionCalls[_functionName].insert(*_node->functionCall);
|
||||||
|
if (m_processedNodes[_functionName].insert(_node).second)
|
||||||
|
m_pendingNodes.at(_functionName).push_front(_node);
|
||||||
|
}
|
||||||
|
|
130
libyul/ControlFlowSideEffectsCollector.h
Normal file
130
libyul/ControlFlowSideEffectsCollector.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
#include <libyul/ControlFlowSideEffects.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <stack>
|
||||||
|
#include <optional>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Dialect;
|
||||||
|
|
||||||
|
struct ControlFlowNode
|
||||||
|
{
|
||||||
|
std::vector<ControlFlowNode const*> successors;
|
||||||
|
/// Name of the called function if the node calls a function.
|
||||||
|
std::optional<YulString> functionCall;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The control flow of a function with entry and exit nodes.
|
||||||
|
*/
|
||||||
|
struct FunctionFlow
|
||||||
|
{
|
||||||
|
ControlFlowNode const* entry;
|
||||||
|
ControlFlowNode const* exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires: Disambiguator, Function Hoister.
|
||||||
|
*/
|
||||||
|
class ControlFlowBuilder: private ASTWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Computes the control-flows of all function defined in the block.
|
||||||
|
/// Assumes the functions are hoisted to the topmost block.
|
||||||
|
explicit ControlFlowBuilder(Block const& _ast);
|
||||||
|
std::map<YulString, FunctionFlow> const& functionFlows() const { return m_functionFlows; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ASTWalker::operator();
|
||||||
|
void operator()(FunctionCall const& _functionCall) override;
|
||||||
|
void operator()(If const& _if) override;
|
||||||
|
void operator()(Switch const& _switch) override;
|
||||||
|
void operator()(FunctionDefinition const& _functionDefinition) override;
|
||||||
|
void operator()(ForLoop const& _forLoop) override;
|
||||||
|
void operator()(Break const& _break) override;
|
||||||
|
void operator()(Continue const& _continue) override;
|
||||||
|
void operator()(Leave const& _leaveStatement) override;
|
||||||
|
|
||||||
|
void newConnectedNode();
|
||||||
|
ControlFlowNode* newNode();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<ControlFlowNode>> m_nodes;
|
||||||
|
|
||||||
|
ControlFlowNode* m_currentNode = nullptr;
|
||||||
|
ControlFlowNode const* m_leave = nullptr;
|
||||||
|
ControlFlowNode const* m_break = nullptr;
|
||||||
|
ControlFlowNode const* m_continue = nullptr;
|
||||||
|
|
||||||
|
std::map<YulString, FunctionFlow> m_functionFlows;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires: Disambiguator, Function Hoister.
|
||||||
|
*/
|
||||||
|
class ControlFlowSideEffectsCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ControlFlowSideEffectsCollector(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block const& _ast
|
||||||
|
);
|
||||||
|
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& functionSideEffects() const
|
||||||
|
{
|
||||||
|
return m_functionSideEffects;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @returns false if nothing could be processed.
|
||||||
|
bool processFunction(YulString _name);
|
||||||
|
|
||||||
|
/// @returns the next pending node of the function that is not
|
||||||
|
/// a function call to a function that might not continue.
|
||||||
|
/// De-queues the node or returns nullptr if no such node is found.
|
||||||
|
ControlFlowNode const* nextProcessableNode(YulString _functionName);
|
||||||
|
|
||||||
|
/// @returns the side-effects of either a builtin call or a user defined function
|
||||||
|
/// call (as far as already computed).
|
||||||
|
ControlFlowSideEffects const& sideEffects(YulString _functionName) const;
|
||||||
|
|
||||||
|
/// Queues the given node to be processed (if not already visited)
|
||||||
|
/// and if it is a function call, records that `_functionName` calls
|
||||||
|
/// `*_node->functionCall`.
|
||||||
|
void recordReachabilityAndQueue(YulString _functionName, ControlFlowNode const* _node);
|
||||||
|
|
||||||
|
Dialect const& m_dialect;
|
||||||
|
ControlFlowBuilder m_cfgBuilder;
|
||||||
|
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
|
||||||
|
std::map<YulString, std::list<ControlFlowNode const*>> m_pendingNodes;
|
||||||
|
std::map<YulString, std::set<ControlFlowNode const*>> m_processedNodes;
|
||||||
|
/// `x` is in `m_functionCalls[y]` if a direct call to `x` is reachable inside `y`
|
||||||
|
std::map<YulString, std::set<YulString>> m_functionCalls;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -22,8 +22,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libyul/YulString.h>
|
#include <libyul/YulString.h>
|
||||||
#include <libyul/SideEffects.h>
|
|
||||||
#include <libyul/ControlFlowSideEffects.h>
|
#include <libyul/ControlFlowSideEffects.h>
|
||||||
|
#include <libyul/SideEffects.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -51,13 +51,14 @@ string indent(std::string const& _input)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string Data::toString(Dialect const*, CharStreamProvider const*) const
|
string Data::toString(Dialect const*, DebugInfoSelection const&, CharStreamProvider const*) const
|
||||||
{
|
{
|
||||||
return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\"";
|
return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
string Object::toString(
|
string Object::toString(
|
||||||
Dialect const* _dialect,
|
Dialect const* _dialect,
|
||||||
|
DebugInfoSelection const& _debugInfoSelection,
|
||||||
CharStreamProvider const* _soliditySourceProvider
|
CharStreamProvider const* _soliditySourceProvider
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
@ -74,10 +75,15 @@ string Object::toString(
|
|||||||
})) +
|
})) +
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
string inner = "code " + AsmPrinter{_dialect, debugData->sourceNames, _soliditySourceProvider}(*code);
|
string inner = "code " + AsmPrinter(
|
||||||
|
_dialect,
|
||||||
|
debugData->sourceNames,
|
||||||
|
_debugInfoSelection,
|
||||||
|
_soliditySourceProvider
|
||||||
|
)(*code);
|
||||||
|
|
||||||
for (auto const& obj: subObjects)
|
for (auto const& obj: subObjects)
|
||||||
inner += "\n" + obj->toString(_dialect, _soliditySourceProvider);
|
inner += "\n" + obj->toString(_dialect, _debugInfoSelection, _soliditySourceProvider);
|
||||||
|
|
||||||
return useSrcComment + "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
|
return useSrcComment + "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libyul/YulString.h>
|
#include <libyul/YulString.h>
|
||||||
|
|
||||||
#include <liblangutil/CharStreamProvider.h>
|
#include <liblangutil/CharStreamProvider.h>
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ struct ObjectNode
|
|||||||
YulString name;
|
YulString name;
|
||||||
virtual std::string toString(
|
virtual std::string toString(
|
||||||
Dialect const* _dialect,
|
Dialect const* _dialect,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection,
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider
|
langutil::CharStreamProvider const* _soliditySourceProvider
|
||||||
) const = 0;
|
) const = 0;
|
||||||
};
|
};
|
||||||
@ -69,6 +71,7 @@ struct Data: public ObjectNode
|
|||||||
|
|
||||||
std::string toString(
|
std::string toString(
|
||||||
Dialect const* _dialect,
|
Dialect const* _dialect,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection,
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider
|
langutil::CharStreamProvider const* _soliditySourceProvider
|
||||||
) const override;
|
) const override;
|
||||||
};
|
};
|
||||||
@ -89,6 +92,7 @@ public:
|
|||||||
/// @returns a (parseable) string representation.
|
/// @returns a (parseable) string representation.
|
||||||
std::string toString(
|
std::string toString(
|
||||||
Dialect const* _dialect,
|
Dialect const* _dialect,
|
||||||
|
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
|
||||||
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -166,6 +168,26 @@ inline YulString operator "" _yulstring(char const* _string, std::size_t _size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace fmt
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct formatter<solidity::yul::YulString>
|
||||||
|
{
|
||||||
|
template <typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext& _context)
|
||||||
|
{
|
||||||
|
return _context.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(solidity::yul::YulString _value, FormatContext& _context)
|
||||||
|
{
|
||||||
|
return format_to(_context.out(), "{}", _value.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
template<> struct hash<solidity::yul::YulString>
|
template<> struct hash<solidity::yul::YulString>
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
#include <libyul/AsmPrinter.h>
|
|
||||||
|
|
||||||
#include <libsolutil/cxx20.h>
|
#include <libsolutil/cxx20.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
@ -230,7 +229,7 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
|
|||||||
// not only for builtins.
|
// not only for builtins.
|
||||||
if (auto const* funCall = get_if<FunctionCall>(&_exprStmt.expression))
|
if (auto const* funCall = get_if<FunctionCall>(&_exprStmt.expression))
|
||||||
if (BuiltinFunction const* builtin = m_dialect.builtin(funCall->functionName.name))
|
if (BuiltinFunction const* builtin = m_dialect.builtin(funCall->functionName.name))
|
||||||
if (builtin->controlFlowSideEffects.terminates)
|
if (builtin->controlFlowSideEffects.terminatesOrReverts())
|
||||||
{
|
{
|
||||||
m_currentBlock->exit = CFG::BasicBlock::Terminated{};
|
m_currentBlock->exit = CFG::BasicBlock::Terminated{};
|
||||||
m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock));
|
m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock));
|
||||||
|
@ -57,8 +57,20 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
|
|||||||
f.parameters.resize(static_cast<size_t>(info.args));
|
f.parameters.resize(static_cast<size_t>(info.args));
|
||||||
f.returns.resize(static_cast<size_t>(info.ret));
|
f.returns.resize(static_cast<size_t>(info.ret));
|
||||||
f.sideEffects = EVMDialect::sideEffectsOfInstruction(_instruction);
|
f.sideEffects = EVMDialect::sideEffectsOfInstruction(_instruction);
|
||||||
f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction);
|
if (evmasm::SemanticInformation::terminatesControlFlow(_instruction))
|
||||||
f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction);
|
{
|
||||||
|
f.controlFlowSideEffects.canContinue = false;
|
||||||
|
if (evmasm::SemanticInformation::reverts(_instruction))
|
||||||
|
{
|
||||||
|
f.controlFlowSideEffects.canTerminate = false;
|
||||||
|
f.controlFlowSideEffects.canRevert = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
f.controlFlowSideEffects.canTerminate = true;
|
||||||
|
f.controlFlowSideEffects.canRevert = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
f.isMSize = _instruction == evmasm::Instruction::MSIZE;
|
f.isMSize = _instruction == evmasm::Instruction::MSIZE;
|
||||||
f.literalArguments.clear();
|
f.literalArguments.clear();
|
||||||
f.instruction = _instruction;
|
f.instruction = _instruction;
|
||||||
|
@ -484,7 +484,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
|
|||||||
yulAssert(!_block.operations.empty(), "");
|
yulAssert(!_block.operations.empty(), "");
|
||||||
CFG::BuiltinCall const* builtinCall = get_if<CFG::BuiltinCall>(&_block.operations.back().operation);
|
CFG::BuiltinCall const* builtinCall = get_if<CFG::BuiltinCall>(&_block.operations.back().operation);
|
||||||
yulAssert(builtinCall, "");
|
yulAssert(builtinCall, "");
|
||||||
yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminates, "");
|
yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminatesOrReverts(), "");
|
||||||
}
|
}
|
||||||
}, _block.exit);
|
}, _block.exit);
|
||||||
// TODO: We could assert that the last emitted assembly item terminated or was an (unconditional) jump.
|
// TODO: We could assert that the last emitted assembly item terminated or was an (unconditional) jump.
|
||||||
|
@ -129,8 +129,9 @@ WasmDialect::WasmDialect()
|
|||||||
m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None;
|
m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None;
|
||||||
m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None;
|
m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None;
|
||||||
m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None;
|
m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None;
|
||||||
m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true;
|
m_functions["unreachable"_yulstring].controlFlowSideEffects.canTerminate = false;
|
||||||
m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true;
|
m_functions["unreachable"_yulstring].controlFlowSideEffects.canRevert = true;
|
||||||
|
m_functions["unreachable"_yulstring].controlFlowSideEffects.canContinue = false;
|
||||||
|
|
||||||
addFunction("datasize", {i64}, {i64}, true, {LiteralKind::String});
|
addFunction("datasize", {i64}, {i64}, true, {LiteralKind::String});
|
||||||
addFunction("dataoffset", {i64}, {i64}, true, {LiteralKind::String});
|
addFunction("dataoffset", {i64}, {i64}, true, {LiteralKind::String});
|
||||||
@ -215,11 +216,11 @@ void WasmDialect::addExternals()
|
|||||||
{"eth", "log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}},
|
{"eth", "log", {i32ptr, i32, i32, i32ptr, i32ptr, i32ptr, i32ptr}, {}},
|
||||||
{"eth", "getBlockNumber", {}, {i64}},
|
{"eth", "getBlockNumber", {}, {i64}},
|
||||||
{"eth", "getTxOrigin", {i32ptr}, {}},
|
{"eth", "getTxOrigin", {i32ptr}, {}},
|
||||||
{"eth", "finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false}},
|
{"eth", "finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false, false}},
|
||||||
{"eth", "revert", {i32ptr, i32}, {}, ControlFlowSideEffects{true, true}},
|
{"eth", "revert", {i32ptr, i32}, {}, ControlFlowSideEffects{false, true, false}},
|
||||||
{"eth", "getReturnDataSize", {}, {i32}},
|
{"eth", "getReturnDataSize", {}, {i32}},
|
||||||
{"eth", "returnDataCopy", {i32ptr, i32, i32}, {}},
|
{"eth", "returnDataCopy", {i32ptr, i32, i32}, {}},
|
||||||
{"eth", "selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{true, false}},
|
{"eth", "selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{false, true, false}},
|
||||||
{"eth", "getBlockTimestamp", {}, {i64}},
|
{"eth", "getBlockTimestamp", {}, {i64}},
|
||||||
{"debug", "print32", {i32}, {}},
|
{"debug", "print32", {i32}, {}},
|
||||||
{"debug", "print64", {i64}, {}},
|
{"debug", "print64", {i64}, {}},
|
||||||
@ -240,7 +241,7 @@ void WasmDialect::addExternals()
|
|||||||
// TODO some of them are side effect free.
|
// TODO some of them are side effect free.
|
||||||
f.sideEffects = SideEffects::worst();
|
f.sideEffects = SideEffects::worst();
|
||||||
f.sideEffects.cannotLoop = true;
|
f.sideEffects.cannotLoop = true;
|
||||||
f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminates;
|
f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminatesOrReverts();
|
||||||
f.controlFlowSideEffects = ext.controlFlowSideEffects;
|
f.controlFlowSideEffects = ext.controlFlowSideEffects;
|
||||||
f.isMSize = false;
|
f.isMSize = false;
|
||||||
f.literalArguments.clear();
|
f.literalArguments.clear();
|
||||||
|
@ -22,7 +22,7 @@ using namespace std;
|
|||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
|
|
||||||
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block& _block)
|
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block const& _block)
|
||||||
{
|
{
|
||||||
FunctionDefinitionCollector functionDefinitionCollector;
|
FunctionDefinitionCollector functionDefinitionCollector;
|
||||||
functionDefinitionCollector(_block);
|
functionDefinitionCollector(_block);
|
||||||
|
@ -34,7 +34,7 @@ namespace solidity::yul
|
|||||||
class FunctionDefinitionCollector: ASTWalker
|
class FunctionDefinitionCollector: ASTWalker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::map<YulString, FunctionDefinition const*> run(Block& _block);
|
static std::map<YulString, FunctionDefinition const*> run(Block const& _block);
|
||||||
private:
|
private:
|
||||||
using ASTWalker::operator();
|
using ASTWalker::operator();
|
||||||
void operator()(FunctionDefinition const& _functionDefinition) override;
|
void operator()(FunctionDefinition const& _functionDefinition) override;
|
||||||
|
@ -78,6 +78,9 @@
|
|||||||
#include <range/v3/view/map.hpp>
|
#include <range/v3/view/map.hpp>
|
||||||
#include <range/v3/action/remove.hpp>
|
#include <range/v3/action/remove.hpp>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
@ -87,7 +90,7 @@ void OptimiserSuite::run(
|
|||||||
GasMeter const* _meter,
|
GasMeter const* _meter,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
string const& _optimisationSequence,
|
string_view _optimisationSequence,
|
||||||
optional<size_t> _expectedExecutionsPerDeployment,
|
optional<size_t> _expectedExecutionsPerDeployment,
|
||||||
set<YulString> const& _externallyUsedIdentifiers
|
set<YulString> const& _externallyUsedIdentifiers
|
||||||
)
|
)
|
||||||
@ -267,9 +270,9 @@ map<char, string> const& OptimiserSuite::stepAbbreviationToNameMap()
|
|||||||
return lookupTable;
|
return lookupTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptimiserSuite::validateSequence(string const& _stepAbbreviations)
|
void OptimiserSuite::validateSequence(string_view _stepAbbreviations)
|
||||||
{
|
{
|
||||||
bool insideLoop = false;
|
int8_t nestingLevel = 0;
|
||||||
for (char abbreviation: _stepAbbreviations)
|
for (char abbreviation: _stepAbbreviations)
|
||||||
switch (abbreviation)
|
switch (abbreviation)
|
||||||
{
|
{
|
||||||
@ -277,12 +280,12 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations)
|
|||||||
case '\n':
|
case '\n':
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
assertThrow(!insideLoop, OptimizerException, "Nested brackets are not supported");
|
assertThrow(nestingLevel < numeric_limits<int8_t>::max(), OptimizerException, "Brackets nested too deep");
|
||||||
insideLoop = true;
|
nestingLevel++;
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
assertThrow(insideLoop, OptimizerException, "Unbalanced brackets");
|
nestingLevel--;
|
||||||
insideLoop = false;
|
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@ -301,43 +304,99 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations)
|
|||||||
OptimizerException,
|
OptimizerException,
|
||||||
"'"s + abbreviation + "' is invalid in the current environment: " + *invalid
|
"'"s + abbreviation + "' is invalid in the current environment: " + *invalid
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThrow(!insideLoop, OptimizerException, "Unbalanced brackets");
|
assertThrow(nestingLevel == 0, OptimizerException, "Unbalanced brackets");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptimiserSuite::runSequence(string const& _stepAbbreviations, Block& _ast)
|
void OptimiserSuite::runSequence(string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable)
|
||||||
{
|
{
|
||||||
validateSequence(_stepAbbreviations);
|
validateSequence(_stepAbbreviations);
|
||||||
|
|
||||||
string input = _stepAbbreviations;
|
// This splits 'aaa[bbb]ccc...' into 'aaa' and '[bbb]ccc...'.
|
||||||
ranges::actions::remove(input, ' ');
|
auto extractNonNestedPrefix = [](string_view _tail) -> tuple<string_view, string_view>
|
||||||
ranges::actions::remove(input, '\n');
|
{
|
||||||
|
for (size_t i = 0; i < _tail.size(); ++i)
|
||||||
|
{
|
||||||
|
yulAssert(_tail[i] != ']');
|
||||||
|
if (_tail[i] == '[')
|
||||||
|
return {_tail.substr(0, i), _tail.substr(i)};
|
||||||
|
}
|
||||||
|
return {_tail, {}};
|
||||||
|
};
|
||||||
|
|
||||||
auto abbreviationsToSteps = [](string const& _sequence) -> vector<string>
|
// This splits '[bbb]ccc...' into 'bbb' and 'ccc...'.
|
||||||
|
auto extractBracketContent = [](string_view _tail) -> tuple<string_view, string_view>
|
||||||
|
{
|
||||||
|
yulAssert(!_tail.empty() && _tail[0] == '[');
|
||||||
|
|
||||||
|
size_t contentLength = 0;
|
||||||
|
int8_t nestingLevel = 1;
|
||||||
|
for (char abbreviation: _tail.substr(1))
|
||||||
|
{
|
||||||
|
if (abbreviation == '[')
|
||||||
|
{
|
||||||
|
yulAssert(nestingLevel < numeric_limits<int8_t>::max());
|
||||||
|
++nestingLevel;
|
||||||
|
}
|
||||||
|
else if (abbreviation == ']')
|
||||||
|
{
|
||||||
|
--nestingLevel;
|
||||||
|
if (nestingLevel == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++contentLength;
|
||||||
|
}
|
||||||
|
yulAssert(nestingLevel == 0);
|
||||||
|
yulAssert(_tail[contentLength + 1] == ']');
|
||||||
|
|
||||||
|
return {_tail.substr(1, contentLength), _tail.substr(contentLength + 2)};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto abbreviationsToSteps = [](string_view _sequence) -> vector<string>
|
||||||
{
|
{
|
||||||
vector<string> steps;
|
vector<string> steps;
|
||||||
for (char abbreviation: _sequence)
|
for (char abbreviation: _sequence)
|
||||||
|
if (abbreviation != ' ' && abbreviation != '\n')
|
||||||
steps.emplace_back(stepAbbreviationToNameMap().at(abbreviation));
|
steps.emplace_back(stepAbbreviationToNameMap().at(abbreviation));
|
||||||
return steps;
|
return steps;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The sequence has now been validated and must consist of pairs of segments that look like this: `aaa[bbb]`
|
vector<tuple<string_view, bool>> subsequences;
|
||||||
// `aaa` or `[bbb]` can be empty. For example we consider a sequence like `fgo[aaf]Oo` to have
|
string_view tail = _stepAbbreviations;
|
||||||
// four segments, the last of which is an empty bracket.
|
while (!tail.empty())
|
||||||
size_t currentPairStart = 0;
|
|
||||||
while (currentPairStart < input.size())
|
|
||||||
{
|
{
|
||||||
size_t openingBracket = input.find('[', currentPairStart);
|
string_view subsequence;
|
||||||
size_t closingBracket = input.find(']', openingBracket);
|
tie(subsequence, tail) = extractNonNestedPrefix(tail);
|
||||||
size_t firstCharInside = (openingBracket == string::npos ? input.size() : openingBracket + 1);
|
if (subsequence.size() > 0)
|
||||||
yulAssert((openingBracket == string::npos) == (closingBracket == string::npos), "");
|
subsequences.push_back({subsequence, false});
|
||||||
|
|
||||||
runSequence(abbreviationsToSteps(input.substr(currentPairStart, openingBracket - currentPairStart)), _ast);
|
if (tail.empty())
|
||||||
runSequenceUntilStable(abbreviationsToSteps(input.substr(firstCharInside, closingBracket - firstCharInside)), _ast);
|
break;
|
||||||
|
|
||||||
currentPairStart = (closingBracket == string::npos ? input.size() : closingBracket + 1);
|
tie(subsequence, tail) = extractBracketContent(tail);
|
||||||
|
if (subsequence.size() > 0)
|
||||||
|
subsequences.push_back({subsequence, true});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t codeSize = 0;
|
||||||
|
for (size_t round = 0; round < MaxRounds; ++round)
|
||||||
|
{
|
||||||
|
for (auto const& [subsequence, repeat]: subsequences)
|
||||||
|
{
|
||||||
|
if (repeat)
|
||||||
|
runSequence(subsequence, _ast, true);
|
||||||
|
else
|
||||||
|
runSequence(abbreviationsToSteps(subsequence), _ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_repeatUntilStable)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size_t newSize = CodeSize::codeSizeIncludingFunctions(_ast);
|
||||||
|
if (newSize == codeSize)
|
||||||
|
break;
|
||||||
|
codeSize = newSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,24 +424,3 @@ void OptimiserSuite::runSequence(std::vector<string> const& _steps, Block& _ast)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptimiserSuite::runSequenceUntilStable(
|
|
||||||
std::vector<string> const& _steps,
|
|
||||||
Block& _ast,
|
|
||||||
size_t maxRounds
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (_steps.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t codeSize = 0;
|
|
||||||
for (size_t rounds = 0; rounds < maxRounds; ++rounds)
|
|
||||||
{
|
|
||||||
size_t newSize = CodeSize::codeSizeIncludingFunctions(_ast);
|
|
||||||
if (newSize == codeSize)
|
|
||||||
break;
|
|
||||||
codeSize = newSize;
|
|
||||||
|
|
||||||
runSequence(_steps, _ast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
@ -64,22 +65,17 @@ public:
|
|||||||
GasMeter const* _meter,
|
GasMeter const* _meter,
|
||||||
Object& _object,
|
Object& _object,
|
||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
std::string const& _optimisationSequence,
|
std::string_view _optimisationSequence,
|
||||||
std::optional<size_t> _expectedExecutionsPerDeployment,
|
std::optional<size_t> _expectedExecutionsPerDeployment,
|
||||||
std::set<YulString> const& _externallyUsedIdentifiers = {}
|
std::set<YulString> const& _externallyUsedIdentifiers = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Ensures that specified sequence of step abbreviations is well-formed and can be executed.
|
/// Ensures that specified sequence of step abbreviations is well-formed and can be executed.
|
||||||
/// @throw OptimizerException if the sequence is invalid
|
/// @throw OptimizerException if the sequence is invalid
|
||||||
static void validateSequence(std::string const& _stepAbbreviations);
|
static void validateSequence(std::string_view _stepAbbreviations);
|
||||||
|
|
||||||
void runSequence(std::vector<std::string> const& _steps, Block& _ast);
|
void runSequence(std::vector<std::string> const& _steps, Block& _ast);
|
||||||
void runSequence(std::string const& _stepAbbreviations, Block& _ast);
|
void runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable = false);
|
||||||
void runSequenceUntilStable(
|
|
||||||
std::vector<std::string> const& _steps,
|
|
||||||
Block& _ast,
|
|
||||||
size_t maxRounds = MaxRounds
|
|
||||||
);
|
|
||||||
|
|
||||||
static std::map<std::string, std::unique_ptr<OptimiserStep>> const& allSteps();
|
static std::map<std::string, std::unique_ptr<OptimiserStep>> const& allSteps();
|
||||||
static std::map<std::string, char> const& stepNameToAbbreviationMap();
|
static std::map<std::string, char> const& stepNameToAbbreviationMap();
|
||||||
|
@ -413,7 +413,7 @@ def commandline_parser() -> ArgumentParser:
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Immediately exit and print compiler output if the compiler exits with an error.",
|
help="Immediately exit and print compiler output if the compiler exits with an error.",
|
||||||
)
|
)
|
||||||
return parser;
|
return parser
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
YULARGS=(--strict-assembly)
|
YULARGS=(--strict-assembly)
|
||||||
FULLARGS=(--optimize --ignore-missing --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
FULLARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
||||||
OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
||||||
function compileFull()
|
function compileFull()
|
||||||
{
|
{
|
||||||
@ -99,7 +99,7 @@ function compileFull()
|
|||||||
printError "Inside directory:"
|
printError "Inside directory:"
|
||||||
echo " $(pwd)"
|
echo " $(pwd)"
|
||||||
printError "Input was:"
|
printError "Input was:"
|
||||||
cat -- "${files[@]}"
|
echo "${files[@]}"
|
||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ def colorize(left, right, id):
|
|||||||
reset = "\x1b[0m"
|
reset = "\x1b[0m"
|
||||||
colors = [red, yellow]
|
colors = [red, yellow]
|
||||||
color = colors[id % len(colors)]
|
color = colors[id % len(colors)]
|
||||||
function, arguments, results = parse_call(right)
|
function, _arguments, _results = parse_call(right)
|
||||||
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
|
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
|
||||||
right = right.replace("constructor", color + "constructor" + reset)
|
right = right.replace("constructor", color + "constructor" + reset)
|
||||||
if function:
|
if function:
|
||||||
@ -158,7 +158,7 @@ def main(argv):
|
|||||||
interactive = False
|
interactive = False
|
||||||
input_file = None
|
input_file = None
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(argv, "if:")
|
opts, _args = getopt.getopt(argv, "if:")
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
|
print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -158,7 +158,7 @@ class TraceAnalyser:
|
|||||||
for trace_id, trace in enumerate(left.traces):
|
for trace_id, trace in enumerate(left.traces):
|
||||||
left_trace = trace
|
left_trace = trace
|
||||||
right_trace = right.traces[trace_id]
|
right_trace = right.traces[trace_id]
|
||||||
assert (left_trace.kind == right_trace.kind)
|
assert left_trace.kind == right_trace.kind
|
||||||
if str(left_trace) != str(right_trace):
|
if str(left_trace) != str(right_trace):
|
||||||
mismatch_info = " " + str(left_trace) + "\n"
|
mismatch_info = " " + str(left_trace) + "\n"
|
||||||
mismatch_info += " " + str(right_trace) + "\n"
|
mismatch_info += " " + str(right_trace) + "\n"
|
||||||
@ -179,7 +179,7 @@ def main(argv):
|
|||||||
extracted_tests_trace_file = None
|
extracted_tests_trace_file = None
|
||||||
end_to_end_trace_file = None
|
end_to_end_trace_file = None
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(argv, "s:e:")
|
opts, _args = getopt.getopt(argv, "s:e:")
|
||||||
except getopt.GetoptError:
|
except getopt.GetoptError:
|
||||||
print("verify-testcases.py [-s <path to semantic-trace>] [-e <path to endToEndExtraction-trace>]")
|
print("verify-testcases.py [-s <path to semantic-trace>] [-e <path to endToEndExtraction-trace>]")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
@ -261,9 +261,9 @@ def main(argv):
|
|||||||
no_confirm = False
|
no_confirm = False
|
||||||
examine_coverage = False
|
examine_coverage = False
|
||||||
next_id = False
|
next_id = False
|
||||||
opts, args = getopt.getopt(argv, "", ["check", "fix", "no-confirm", "examine-coverage", "next"])
|
opts, _args = getopt.getopt(argv, "", ["check", "fix", "no-confirm", "examine-coverage", "next"])
|
||||||
|
|
||||||
for opt, arg in opts:
|
for opt, _arg in opts:
|
||||||
if opt == "--check":
|
if opt == "--check":
|
||||||
check = True
|
check = True
|
||||||
elif opt == "--fix":
|
elif opt == "--fix":
|
||||||
|
@ -50,8 +50,9 @@ def readDependencies(fname):
|
|||||||
library = line.split(' ', 1)[0][1:]
|
library = line.split(' ', 1)[0][1:]
|
||||||
if (library.startswith("/usr/local/lib") or
|
if (library.startswith("/usr/local/lib") or
|
||||||
library.startswith("/usr/local/opt") or
|
library.startswith("/usr/local/opt") or
|
||||||
library.startswith("/Users/")):
|
library.startswith("/Users/")
|
||||||
if (os.path.basename(library) != os.path.basename(fname)):
|
):
|
||||||
|
if os.path.basename(library) != os.path.basename(fname):
|
||||||
command = "install_name_tool -change " + \
|
command = "install_name_tool -change " + \
|
||||||
library + " @executable_path/./" + \
|
library + " @executable_path/./" + \
|
||||||
os.path.basename(library) + " " + fname
|
os.path.basename(library) + " " + fname
|
||||||
|
@ -20,7 +20,7 @@ repository. The changes are compared against ``origin/develop``.
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from parsec import *
|
from parsec import generate, ParseError, regex, string
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
class Kind(Enum):
|
class Kind(Enum):
|
||||||
@ -56,10 +56,10 @@ def diff_string() -> (Kind, Diff, int):
|
|||||||
-// gas irOptimized: 138070
|
-// gas irOptimized: 138070
|
||||||
|
|
||||||
"""
|
"""
|
||||||
diff_kind = yield (minus | plus)
|
diff_kind = yield minus | plus
|
||||||
yield comment
|
yield comment
|
||||||
yield space
|
yield space
|
||||||
codegen_kind = yield (gas_ir_optimized ^ gas_legacy_optimized ^ gas_legacy)
|
codegen_kind = yield gas_ir_optimized ^ gas_legacy_optimized ^ gas_legacy
|
||||||
yield colon
|
yield colon
|
||||||
yield space
|
yield space
|
||||||
val = yield number()
|
val = yield number()
|
||||||
|
@ -179,16 +179,6 @@ case $(uname -s) in
|
|||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
install_z3=""
|
install_z3=""
|
||||||
case $VERSION_ID in
|
case $VERSION_ID in
|
||||||
7)
|
|
||||||
#wheezy
|
|
||||||
echo "Installing solidity dependencies on Debian Wheezy (7.x)."
|
|
||||||
echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet."
|
|
||||||
echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions."
|
|
||||||
echo "If you would like to get 'install_deps.sh' working for Debian Wheezy, that would be fantastic."
|
|
||||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev."
|
|
||||||
echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
8)
|
8)
|
||||||
#jessie
|
#jessie
|
||||||
echo "Installing solidity dependencies on Debian Jesse (8.x)."
|
echo "Installing solidity dependencies on Debian Jesse (8.x)."
|
||||||
@ -277,32 +267,11 @@ case $(uname -s) in
|
|||||||
echo "Installing solidity dependencies on Ubuntu Trusty Tahr (14.04)."
|
echo "Installing solidity dependencies on Ubuntu Trusty Tahr (14.04)."
|
||||||
echo "Or, you may also be running Linux Mint Qiana / Rebecca / Rafaela / Rosa (base: Ubuntu Trusty Tahr (14.04).)"
|
echo "Or, you may also be running Linux Mint Qiana / Rebecca / Rafaela / Rosa (base: Ubuntu Trusty Tahr (14.04).)"
|
||||||
;;
|
;;
|
||||||
utopic)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Utopic Unicorn (14.10)."
|
|
||||||
;;
|
|
||||||
vivid)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Vivid Vervet (15.04)."
|
|
||||||
;;
|
|
||||||
wily)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Wily Werewolf (15.10)."
|
|
||||||
;;
|
|
||||||
xenial|sarah|serena|sonya|sylvia)
|
xenial|sarah|serena|sonya|sylvia)
|
||||||
echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)."
|
echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)."
|
||||||
echo "Or, you may also be running Linux Mint Sarah / Serena / Sonya / Sylvia (base: Ubuntu Xenial Xerus (16.04).)"
|
echo "Or, you may also be running Linux Mint Sarah / Serena / Sonya / Sylvia (base: Ubuntu Xenial Xerus (16.04).)"
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
;;
|
;;
|
||||||
yakkety)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Yakkety Yak (16.10)."
|
|
||||||
install_z3="libz3-dev"
|
|
||||||
;;
|
|
||||||
zesty)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Zesty (17.04)."
|
|
||||||
install_z3="libz3-dev"
|
|
||||||
;;
|
|
||||||
artful)
|
|
||||||
echo "Installing solidity dependencies on Ubuntu Artful (17.10)."
|
|
||||||
install_z3="libz3-dev"
|
|
||||||
;;
|
|
||||||
bionic)
|
bionic)
|
||||||
echo "Installing solidity dependencies."
|
echo "Installing solidity dependencies."
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
@ -311,6 +280,10 @@ case $(uname -s) in
|
|||||||
echo "Installing solidity dependencies."
|
echo "Installing solidity dependencies."
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
;;
|
;;
|
||||||
|
hirsute)
|
||||||
|
echo "Installing solidity dependencies."
|
||||||
|
install_z3="libz3-dev"
|
||||||
|
;;
|
||||||
betsy)
|
betsy)
|
||||||
#do not try anything for betsy.
|
#do not try anything for betsy.
|
||||||
echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian."
|
echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian."
|
||||||
@ -325,7 +298,7 @@ case $(uname -s) in
|
|||||||
echo "ERROR - Unknown or unsupported Ubuntu version ($(lsb_release -cs))"
|
echo "ERROR - Unknown or unsupported Ubuntu version ($(lsb_release -cs))"
|
||||||
echo "ERROR - This might not work, but we are trying anyway."
|
echo "ERROR - This might not work, but we are trying anyway."
|
||||||
echo "Please drop us a message at https://gitter.im/ethereum/solidity-dev."
|
echo "Please drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||||
echo "We only support Trusty, Utopic, Vivid, Wily, Xenial, Yakkety, Zesty, Artful and Bionic."
|
echo "We only support Trusty, Xenial, Bionic, Focal, and Hirsute."
|
||||||
install_z3="libz3-dev"
|
install_z3="libz3-dev"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
# into files for e.g. fuzz testing as
|
# into files for e.g. fuzz testing as
|
||||||
# scripts/isolate_tests.py test/libsolidity/*
|
# scripts/isolate_tests.py test/libsolidity/*
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -9,7 +9,6 @@ from os import path, walk
|
|||||||
from sys import exit
|
from sys import exit
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
|
|
||||||
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
|
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
|
||||||
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"
|
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"
|
||||||
|
@ -24,24 +24,15 @@ disable=
|
|||||||
duplicate-code,
|
duplicate-code,
|
||||||
invalid-name,
|
invalid-name,
|
||||||
missing-docstring,
|
missing-docstring,
|
||||||
mixed-indentation,
|
|
||||||
no-else-return,
|
no-else-return,
|
||||||
no-self-use,
|
no-self-use,
|
||||||
pointless-string-statement,
|
pointless-string-statement,
|
||||||
redefined-builtin,
|
redefined-builtin,
|
||||||
redefined-outer-name,
|
redefined-outer-name,
|
||||||
singleton-comparison,
|
singleton-comparison,
|
||||||
superfluous-parens,
|
|
||||||
too-few-public-methods,
|
too-few-public-methods,
|
||||||
trailing-newlines,
|
too-many-public-methods,
|
||||||
undefined-variable,
|
ungrouped-imports
|
||||||
ungrouped-imports,
|
|
||||||
unnecessary-semicolon,
|
|
||||||
unused-import,
|
|
||||||
unused-variable,
|
|
||||||
unused-wildcard-import,
|
|
||||||
useless-object-inheritance,
|
|
||||||
wildcard-import
|
|
||||||
|
|
||||||
[BASIC]
|
[BASIC]
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import time
|
|||||||
|
|
||||||
DESCRIPTION = """Regressor is a tool to run regression tests in a CI env."""
|
DESCRIPTION = """Regressor is a tool to run regression tests in a CI env."""
|
||||||
|
|
||||||
class PrintDotsThread(object):
|
class PrintDotsThread:
|
||||||
"""Prints a dot every "interval" (default is 300) seconds"""
|
"""Prints a dot every "interval" (default is 300) seconds"""
|
||||||
|
|
||||||
def __init__(self, interval=300):
|
def __init__(self, interval=300):
|
||||||
@ -30,7 +30,7 @@ class PrintDotsThread(object):
|
|||||||
print(".")
|
print(".")
|
||||||
time.sleep(self.interval)
|
time.sleep(self.interval)
|
||||||
|
|
||||||
class regressor():
|
class regressor:
|
||||||
_re_sanitizer_log = re.compile(r"""ERROR: (libFuzzer|UndefinedBehaviorSanitizer)""")
|
_re_sanitizer_log = re.compile(r"""ERROR: (libFuzzer|UndefinedBehaviorSanitizer)""")
|
||||||
|
|
||||||
def __init__(self, description, args):
|
def __init__(self, description, args):
|
||||||
|
@ -105,10 +105,11 @@ ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files
|
|||||||
git clone --depth 2 --recursive https://github.com/ethereum/solidity.git -b "$branch"
|
git clone --depth 2 --recursive https://github.com/ethereum/solidity.git -b "$branch"
|
||||||
mv solidity solc
|
mv solidity solc
|
||||||
|
|
||||||
# Fetch jsoncpp dependency
|
# Fetch dependencies
|
||||||
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
|
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
|
||||||
wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz
|
wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz
|
||||||
wget -O ./solc/deps/downloads/range-v3-0.11.0.tar.gz https://github.com/ericniebler/range-v3/archive/0.11.0.tar.gz
|
wget -O ./solc/deps/downloads/range-v3-0.11.0.tar.gz https://github.com/ericniebler/range-v3/archive/0.11.0.tar.gz
|
||||||
|
wget -O ./solc/deps/downloads/fmt-8.0.1.tar.gz https://github.com/fmtlib/fmt/archive/8.0.1.tar.gz
|
||||||
|
|
||||||
# Determine version
|
# Determine version
|
||||||
cd solc
|
cd solc
|
||||||
|
@ -44,9 +44,7 @@ def writeSourceToFile(lines):
|
|||||||
os.system("mkdir -p " + filePath)
|
os.system("mkdir -p " + filePath)
|
||||||
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
|
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
|
||||||
createdSources.append(srcName)
|
createdSources.append(srcName)
|
||||||
i = 0
|
|
||||||
for idx, line in enumerate(lines[1:]):
|
for idx, line in enumerate(lines[1:]):
|
||||||
|
|
||||||
# write to file
|
# write to file
|
||||||
if line[:12] != "==== Source:":
|
if line[:12] != "==== Source:":
|
||||||
f.write(line)
|
f.write(line)
|
||||||
|
@ -7,7 +7,8 @@ import sys
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
from os.path import join, isfile
|
# Pylint for some reason insists that isfile() is unused
|
||||||
|
from os.path import join, isfile # pylint: disable=unused-import
|
||||||
|
|
||||||
|
|
||||||
def extract_test_cases(path):
|
def extract_test_cases(path):
|
||||||
|
@ -41,9 +41,9 @@ parts:
|
|||||||
source-tag: z3-4.8.4
|
source-tag: z3-4.8.4
|
||||||
plugin: make
|
plugin: make
|
||||||
build-packages: [python3]
|
build-packages: [python3]
|
||||||
stage-packages: [libstdc++6]
|
stage-packages: [libstdc++6, libgomp1]
|
||||||
override-build: |
|
override-build: |
|
||||||
python scripts/mk_make.py
|
python3 scripts/mk_make.py
|
||||||
cd build
|
cd build
|
||||||
make -j -l $(grep -c "^processor" /proc/cpuinfo)
|
make -j -l $(grep -c "^processor" /proc/cpuinfo)
|
||||||
make install DESTDIR=$SNAPCRAFT_PART_INSTALL
|
make install DESTDIR=$SNAPCRAFT_PART_INSTALL
|
||||||
|
@ -553,7 +553,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
|
|||||||
if (fs::exists(pathName) && !m_options.output.overwriteFiles)
|
if (fs::exists(pathName) && !m_options.output.overwriteFiles)
|
||||||
{
|
{
|
||||||
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
|
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
|
||||||
m_error = true;
|
m_outputFailed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ofstream outFile(pathName);
|
ofstream outFile(pathName);
|
||||||
@ -561,7 +561,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
|
|||||||
if (!outFile)
|
if (!outFile)
|
||||||
{
|
{
|
||||||
serr() << "Could not write to file \"" << pathName << "\"." << endl;
|
serr() << "Could not write to file \"" << pathName << "\"." << endl;
|
||||||
m_error = true;
|
m_outputFailed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,6 +631,8 @@ bool CommandLineInterface::compile()
|
|||||||
m_compiler->setViaIR(m_options.output.experimentalViaIR);
|
m_compiler->setViaIR(m_options.output.experimentalViaIR);
|
||||||
m_compiler->setEVMVersion(m_options.output.evmVersion);
|
m_compiler->setEVMVersion(m_options.output.evmVersion);
|
||||||
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
|
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
|
||||||
|
if (m_options.output.debugInfoSelection.has_value())
|
||||||
|
m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value());
|
||||||
// TODO: Perhaps we should not compile unless requested
|
// TODO: Perhaps we should not compile unless requested
|
||||||
|
|
||||||
m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized);
|
m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized);
|
||||||
@ -699,30 +701,6 @@ bool CommandLineInterface::compile()
|
|||||||
formatter.printExceptionInformation(_exception, "Compiler error");
|
formatter.printExceptionInformation(_exception, "Compiler error");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (InternalCompilerError const& _exception)
|
|
||||||
{
|
|
||||||
serr() <<
|
|
||||||
"Internal compiler error during compilation:" <<
|
|
||||||
endl <<
|
|
||||||
boost::diagnostic_information(_exception);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (UnimplementedFeatureError const& _exception)
|
|
||||||
{
|
|
||||||
serr() <<
|
|
||||||
"Unimplemented feature:" <<
|
|
||||||
endl <<
|
|
||||||
boost::diagnostic_information(_exception);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (smtutil::SMTLogicError const& _exception)
|
|
||||||
{
|
|
||||||
serr() <<
|
|
||||||
"SMT logic error during analysis:" <<
|
|
||||||
endl <<
|
|
||||||
boost::diagnostic_information(_exception);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (Error const& _error)
|
catch (Error const& _error)
|
||||||
{
|
{
|
||||||
if (_error.type() == Error::Type::DocstringParsingError)
|
if (_error.type() == Error::Type::DocstringParsingError)
|
||||||
@ -735,23 +713,6 @@ bool CommandLineInterface::compile()
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (Exception const& _exception)
|
|
||||||
{
|
|
||||||
serr() << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception during compilation" << (
|
|
||||||
_e.what() ? ": " + string(_e.what()) : "."
|
|
||||||
) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception during compilation." << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -894,7 +855,7 @@ bool CommandLineInterface::actOnInput()
|
|||||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||||
outputCompilationResults();
|
outputCompilationResults();
|
||||||
}
|
}
|
||||||
return !m_error;
|
return !m_outputFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandLineInterface::link()
|
bool CommandLineInterface::link()
|
||||||
@ -976,6 +937,7 @@ void CommandLineInterface::writeLinkedFiles()
|
|||||||
if (!outFile)
|
if (!outFile)
|
||||||
{
|
{
|
||||||
serr() << "Could not write to file " << src.first << ". Aborting." << endl;
|
serr() << "Could not write to file " << src.first << ". Aborting." << endl;
|
||||||
|
m_outputFailed = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1013,35 +975,17 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
|||||||
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
|
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
|
||||||
m_options.output.evmVersion,
|
m_options.output.evmVersion,
|
||||||
_language,
|
_language,
|
||||||
m_options.optimiserSettings()
|
m_options.optimiserSettings(),
|
||||||
|
m_options.output.debugInfoSelection.has_value() ?
|
||||||
|
m_options.output.debugInfoSelection.value() :
|
||||||
|
DebugInfoSelection::Default()
|
||||||
);
|
);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!stack.parseAndAnalyze(src.first, src.second))
|
if (!stack.parseAndAnalyze(src.first, src.second))
|
||||||
successful = false;
|
successful = false;
|
||||||
else
|
else
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
}
|
}
|
||||||
catch (Exception const& _exception)
|
|
||||||
{
|
|
||||||
serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
serr() <<
|
|
||||||
"Unknown exception during compilation" <<
|
|
||||||
(_e.what() ? ": " + string(_e.what()) : ".") <<
|
|
||||||
endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception in assembler." << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& sourceAndStack: assemblyStacks)
|
for (auto const& sourceAndStack: assemblyStacks)
|
||||||
{
|
{
|
||||||
@ -1073,30 +1017,9 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
|||||||
sout() << stack.print() << endl;
|
sout() << stack.print() << endl;
|
||||||
|
|
||||||
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
|
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
stack.translate(yul::AssemblyStack::Language::Ewasm);
|
stack.translate(yul::AssemblyStack::Language::Ewasm);
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
}
|
|
||||||
catch (Exception const& _exception)
|
|
||||||
{
|
|
||||||
serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
serr() <<
|
|
||||||
"Unknown exception during compilation" <<
|
|
||||||
(_e.what() ? ": " + string(_e.what()) : ".") <<
|
|
||||||
endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception in assembler." << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sout() << endl << "==========================" << endl;
|
sout() << endl << "==========================" << endl;
|
||||||
sout() << endl << "Translated source:" << endl;
|
sout() << endl << "Translated source:" << endl;
|
||||||
@ -1104,28 +1027,8 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
|||||||
}
|
}
|
||||||
|
|
||||||
yul::MachineAssemblyObject object;
|
yul::MachineAssemblyObject object;
|
||||||
try
|
|
||||||
{
|
|
||||||
object = stack.assemble(_targetMachine);
|
object = stack.assemble(_targetMachine);
|
||||||
object.bytecode->link(m_options.linker.libraries);
|
object.bytecode->link(m_options.linker.libraries);
|
||||||
}
|
|
||||||
catch (Exception const& _exception)
|
|
||||||
{
|
|
||||||
serr() << "Exception while assembling: " << boost::diagnostic_information(_exception) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception during compilation" << (
|
|
||||||
_e.what() ? ": " + string(_e.what()) : "."
|
|
||||||
) << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
serr() << "Unknown exception while assembling." << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sout() << endl << "Binary representation:" << endl;
|
sout() << endl << "Binary representation:" << endl;
|
||||||
if (object.bytecode)
|
if (object.bytecode)
|
||||||
|
@ -121,7 +121,7 @@ private:
|
|||||||
std::ostream& m_sout;
|
std::ostream& m_sout;
|
||||||
std::ostream& m_serr;
|
std::ostream& m_serr;
|
||||||
bool m_hasOutput = false;
|
bool m_hasOutput = false;
|
||||||
bool m_error = false; ///< If true, some error occurred.
|
bool m_outputFailed = false; ///< If true, creation or write to some of the output files failed.
|
||||||
FileReader m_fileReader;
|
FileReader m_fileReader;
|
||||||
std::optional<std::string> m_standardJsonInput;
|
std::optional<std::string> m_standardJsonInput;
|
||||||
std::unique_ptr<frontend::CompilerStack> m_compiler;
|
std::unique_ptr<frontend::CompilerStack> m_compiler;
|
||||||
|
@ -67,6 +67,7 @@ static string const g_strImportAst = "import-ast";
|
|||||||
static string const g_strInputFile = "input-file";
|
static string const g_strInputFile = "input-file";
|
||||||
static string const g_strYul = "yul";
|
static string const g_strYul = "yul";
|
||||||
static string const g_strYulDialect = "yul-dialect";
|
static string const g_strYulDialect = "yul-dialect";
|
||||||
|
static string const g_strDebugInfo = "debug-info";
|
||||||
static string const g_strIPFS = "ipfs";
|
static string const g_strIPFS = "ipfs";
|
||||||
static string const g_strLicense = "license";
|
static string const g_strLicense = "license";
|
||||||
static string const g_strLibraries = "libraries";
|
static string const g_strLibraries = "libraries";
|
||||||
@ -137,6 +138,14 @@ static set<string> const g_metadataHashArgs
|
|||||||
g_strNone
|
g_strNone
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static map<InputMode, string> const g_inputModeName = {
|
||||||
|
{InputMode::Compiler, "compiler"},
|
||||||
|
{InputMode::CompilerWithASTImport, "compiler (AST import)"},
|
||||||
|
{InputMode::Assembler, "assembler"},
|
||||||
|
{InputMode::StandardJson, "standard JSON"},
|
||||||
|
{InputMode::Linker, "linker"},
|
||||||
|
};
|
||||||
|
|
||||||
void CommandLineParser::printVersionAndExit()
|
void CommandLineParser::printVersionAndExit()
|
||||||
{
|
{
|
||||||
sout() <<
|
sout() <<
|
||||||
@ -244,6 +253,7 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
|
|||||||
output.evmVersion == _other.output.evmVersion &&
|
output.evmVersion == _other.output.evmVersion &&
|
||||||
output.experimentalViaIR == _other.output.experimentalViaIR &&
|
output.experimentalViaIR == _other.output.experimentalViaIR &&
|
||||||
output.revertStrings == _other.output.revertStrings &&
|
output.revertStrings == _other.output.revertStrings &&
|
||||||
|
output.debugInfoSelection == _other.output.debugInfoSelection &&
|
||||||
output.stopAfter == _other.output.stopAfter &&
|
output.stopAfter == _other.output.stopAfter &&
|
||||||
input.mode == _other.input.mode &&
|
input.mode == _other.input.mode &&
|
||||||
assembly.targetMachine == _other.assembly.targetMachine &&
|
assembly.targetMachine == _other.assembly.targetMachine &&
|
||||||
@ -460,6 +470,47 @@ bool CommandLineParser::parseLibraryOption(string const& _input)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommandLineParser::parseOutputSelection()
|
||||||
|
{
|
||||||
|
static auto outputSupported = [](InputMode _mode, string_view _outputName)
|
||||||
|
{
|
||||||
|
static set<string> const compilerModeOutputs =
|
||||||
|
CompilerOutputs::componentMap() |
|
||||||
|
ranges::views::keys |
|
||||||
|
ranges::to<set>();
|
||||||
|
|
||||||
|
switch (_mode)
|
||||||
|
{
|
||||||
|
case InputMode::Compiler:
|
||||||
|
case InputMode::CompilerWithASTImport:
|
||||||
|
return contains(compilerModeOutputs, _outputName);
|
||||||
|
case InputMode::Assembler:
|
||||||
|
case InputMode::StandardJson:
|
||||||
|
case InputMode::Linker:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
solAssert(false, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
|
||||||
|
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0);
|
||||||
|
|
||||||
|
vector<string> unsupportedOutputs;
|
||||||
|
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
|
||||||
|
if (m_options.compiler.outputs.*outputComponent && !outputSupported(m_options.input.mode, optionName))
|
||||||
|
unsupportedOutputs.push_back(optionName);
|
||||||
|
|
||||||
|
if (!unsupportedOutputs.empty())
|
||||||
|
{
|
||||||
|
serr() << "The following outputs are not supported in " << g_inputModeName.at(m_options.input.mode) << " mode: ";
|
||||||
|
serr() << joinOptionNames(unsupportedOutputs) << ".";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
po::options_description CommandLineParser::optionsDescription()
|
po::options_description CommandLineParser::optionsDescription()
|
||||||
{
|
{
|
||||||
// Declare the supported options.
|
// Declare the supported options.
|
||||||
@ -546,6 +597,13 @@ General Information)").c_str(),
|
|||||||
po::value<string>()->value_name(joinHumanReadable(g_revertStringsArgs, ",")),
|
po::value<string>()->value_name(joinHumanReadable(g_revertStringsArgs, ",")),
|
||||||
"Strip revert (and require) reason strings or add additional debugging information."
|
"Strip revert (and require) reason strings or add additional debugging information."
|
||||||
)
|
)
|
||||||
|
(
|
||||||
|
g_strDebugInfo.c_str(),
|
||||||
|
po::value<string>()->default_value(toString(DebugInfoSelection::Default())),
|
||||||
|
("Debug info components to be included in the produced EVM assembly and Yul code. "
|
||||||
|
"Value can be all, none or a comma-separated list containing one or more of the "
|
||||||
|
"following components: " + joinHumanReadable(DebugInfoSelection::componentMap() | ranges::views::keys) + ".").c_str()
|
||||||
|
)
|
||||||
(
|
(
|
||||||
g_strStopAfter.c_str(),
|
g_strStopAfter.c_str(),
|
||||||
po::value<string>()->value_name("stage"),
|
po::value<string>()->value_name("stage"),
|
||||||
@ -886,6 +944,12 @@ bool CommandLineParser::processArgs()
|
|||||||
serr() << "Option --" << option << " is only valid in compiler and assembler modes." << endl;
|
serr() << "Option --" << option << " is only valid in compiler and assembler modes." << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_args[g_strDebugInfo].defaulted())
|
||||||
|
{
|
||||||
|
serr() << "Option --" << g_strDebugInfo << " is only valid in compiler and assembler modes." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_args.count(g_strColor) > 0)
|
if (m_args.count(g_strColor) > 0)
|
||||||
@ -912,6 +976,23 @@ bool CommandLineParser::processArgs()
|
|||||||
m_options.output.revertStrings = *revertStrings;
|
m_options.output.revertStrings = *revertStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_args[g_strDebugInfo].defaulted())
|
||||||
|
{
|
||||||
|
string optionValue = m_args[g_strDebugInfo].as<string>();
|
||||||
|
m_options.output.debugInfoSelection = DebugInfoSelection::fromString(optionValue);
|
||||||
|
if (!m_options.output.debugInfoSelection.has_value())
|
||||||
|
{
|
||||||
|
serr() << "Invalid value for --" << g_strDebugInfo << " option: " << optionValue << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_options.output.debugInfoSelection->snippet && !m_options.output.debugInfoSelection->location)
|
||||||
|
{
|
||||||
|
serr() << "To use 'snippet' with --" << g_strDebugInfo << " you must select also 'location'." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!parseCombinedJsonOption())
|
if (!parseCombinedJsonOption())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -930,8 +1011,8 @@ bool CommandLineParser::processArgs()
|
|||||||
m_options.formatting.json.indent = m_args[g_strJsonIndent].as<uint32_t>();
|
m_options.formatting.json.indent = m_args[g_strJsonIndent].as<uint32_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
|
if (!parseOutputSelection())
|
||||||
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0);
|
return false;
|
||||||
|
|
||||||
m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0);
|
m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0);
|
||||||
|
|
||||||
|
@ -24,8 +24,12 @@
|
|||||||
#include <libsolidity/interface/DebugSettings.h>
|
#include <libsolidity/interface/DebugSettings.h>
|
||||||
#include <libsolidity/interface/FileReader.h>
|
#include <libsolidity/interface/FileReader.h>
|
||||||
#include <libsolidity/interface/ImportRemapper.h>
|
#include <libsolidity/interface/ImportRemapper.h>
|
||||||
|
|
||||||
#include <libyul/AssemblyStack.h>
|
#include <libyul/AssemblyStack.h>
|
||||||
|
|
||||||
|
#include <liblangutil/DebugInfoSelection.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
@ -174,6 +178,7 @@ struct CommandLineOptions
|
|||||||
langutil::EVMVersion evmVersion;
|
langutil::EVMVersion evmVersion;
|
||||||
bool experimentalViaIR = false;
|
bool experimentalViaIR = false;
|
||||||
RevertStrings revertStrings = RevertStrings::Default;
|
RevertStrings revertStrings = RevertStrings::Default;
|
||||||
|
std::optional<langutil::DebugInfoSelection> debugInfoSelection;
|
||||||
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
|
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
|
||||||
} output;
|
} output;
|
||||||
|
|
||||||
@ -286,6 +291,8 @@ private:
|
|||||||
/// @return false if there are any validation errors, true otherwise.
|
/// @return false if there are any validation errors, true otherwise.
|
||||||
bool parseLibraryOption(std::string const& _input);
|
bool parseLibraryOption(std::string const& _input);
|
||||||
|
|
||||||
|
bool parseOutputSelection();
|
||||||
|
|
||||||
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
|
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
|
||||||
[[noreturn]] void printVersionAndExit();
|
[[noreturn]] void printVersionAndExit();
|
||||||
[[noreturn]] void printLicenseAndExit();
|
[[noreturn]] void printLicenseAndExit();
|
||||||
|
@ -22,11 +22,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <solc/CommandLineInterface.h>
|
#include <solc/CommandLineInterface.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/exception/all.hpp>
|
#include <boost/exception/all.hpp>
|
||||||
|
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The equivalent of setlocale(LC_ALL, "C") is called before any user code is run.
|
The equivalent of setlocale(LC_ALL, "C") is called before any user code is run.
|
||||||
@ -53,24 +58,52 @@ static void setDefaultOrCLocale()
|
|||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
setDefaultOrCLocale();
|
|
||||||
solidity::frontend::CommandLineInterface cli(cin, cout, cerr);
|
|
||||||
if (!cli.parseArguments(argc, argv))
|
|
||||||
return 1;
|
|
||||||
if (!cli.readInputFiles())
|
|
||||||
return 1;
|
|
||||||
if (!cli.processInput())
|
|
||||||
return 1;
|
|
||||||
bool success = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
success = cli.actOnInput();
|
setDefaultOrCLocale();
|
||||||
}
|
solidity::frontend::CommandLineInterface cli(cin, cout, cerr);
|
||||||
catch (boost::exception const& _exception)
|
bool success =
|
||||||
{
|
cli.parseArguments(argc, argv) &&
|
||||||
cerr << "Exception during output generation: " << boost::diagnostic_information(_exception) << endl;
|
cli.readInputFiles() &&
|
||||||
success = false;
|
cli.processInput() &&
|
||||||
}
|
cli.actOnInput();
|
||||||
|
|
||||||
return success ? 0 : 1;
|
return success ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
catch (smtutil::SMTLogicError const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "SMT logic error:" << endl;
|
||||||
|
cerr << boost::diagnostic_information(_exception);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (langutil::UnimplementedFeatureError const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "Unimplemented feature:" << endl;
|
||||||
|
cerr << boost::diagnostic_information(_exception);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (langutil::InternalCompilerError const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "Internal compiler error:" << endl;
|
||||||
|
cerr << boost::diagnostic_information(_exception);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (boost::exception const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "Uncaught exception:" << endl;
|
||||||
|
cerr << boost::diagnostic_information(_exception) << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (std::exception const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "Uncaught exception:" << endl;
|
||||||
|
cerr << boost::diagnostic_information(_exception) << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
cerr << "Uncaught exception" << endl;
|
||||||
|
cerr << boost::current_exception_diagnostic_information() << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user