Merge remote-tracking branch 'origin/develop' into HEAD

This commit is contained in:
chriseth 2021-10-19 18:24:42 +02:00
commit e239f62b64
364 changed files with 5168 additions and 893 deletions

View File

@ -627,14 +627,14 @@ jobs:
- checkout
- restore_cache:
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
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
- run:
name: Install build dependencies
command: ./.circleci/osx_install_dependencies.sh
- save_cache:
key: dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
key: dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
paths:
- /usr/local/bin
- /usr/local/sbin
@ -663,7 +663,7 @@ jobs:
- checkout
- restore_cache:
keys:
- dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: .
- run: *run_soltest
@ -679,7 +679,7 @@ jobs:
- checkout
- restore_cache:
keys:
- dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- dependencies-osx-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: .
- run: *run_cmdline_tests
@ -886,14 +886,14 @@ jobs:
- checkout
- restore_cache:
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
# WARNING! If you do edit anything here instead, remember to invalidate the cache manually.
- run:
name: "Installing dependencies"
command: .\scripts\install_deps.ps1
- save_cache:
key: dependencies-win-{{ checksum "scripts/install_deps.ps1" }}
key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }}
paths:
- .\deps
- run:

View File

@ -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
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 install boost
brew install cmake

View File

@ -41,6 +41,7 @@ include(EthCcache)
# Let's find our dependencies
include(EthDependencies)
include(fmtlib)
include(jsoncpp)
include(range-v3)
include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR})

View File

@ -13,12 +13,21 @@ Language 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.
* 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:
* 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: 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``).
* 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
View 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()

View File

@ -1,14 +1,86 @@
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word;
}
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: normal;
white-space: normal;
}
.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;
}

View File

@ -1,11 +1,9 @@
/* links */
a,
a:visited {
color: #aaddff;
.rst-content a:not(:visited) {
color: #aaddff !important;
}
/* code directives */
.method dt,
@ -46,6 +44,9 @@ em.property {
background-color: #5a5a5a;
}
.rst-content pre {
background: none;
}
/* inlined code highlights */
@ -70,6 +71,12 @@ code.docutils.literal.notranslate {
color: #ddd;
}
/* highlight color search text */
.rst-content .highlighted {
background: #ff5722;
box-shadow: 0 0 0 2px #f0978b;
}
/* notes, warnings, hints */
@ -620,3 +627,9 @@ code.docutils.literal.notranslate {
/* 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. */
}

View File

@ -75,3 +75,9 @@ html.transition *:after {
transition: ease-in-out 200ms !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;
}

View 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
View File

@ -0,0 +1,7 @@
{% extends "!footer.html" %}
{% block extrafooter %}
<p>
<a href="{{ pathto('credits-and-attribution') }}">Credits and attribution</a>.
</p>
{% endblock %}

View File

@ -1,6 +1,10 @@
{% extends "!layout.html" %}
{% block menu %}
{% block menu %}
{{ super() }}
<a href="{{ pathto('genindex') }}">Keyword Index</a>
{% endblock %}
<ul>
<li>
<a href="{{ pathto('genindex') }}">Keyword Index</a>
</li>
</ul>
{% endblock %}

View File

@ -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).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::
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
unless you know what you are doing.

View File

@ -44,6 +44,7 @@ def setup(sphinx):
extensions = [
'sphinx_a4doc',
'html_extra_template_renderer',
'remix_code_links',
]
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
# further. For a list of options available for each theme, see the
# 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.
#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
# 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
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@ -238,7 +243,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'solidity.tex', 'Solidity Documentation', 'Ethereum', 'manual'),
('index', 'solidity.tex', 'Solidity Documentation', 'Ethereum', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of

View File

@ -86,12 +86,10 @@ Running the Compiler Tests
Prerequisites
-------------
Some tests require the `evmone <https://github.com/ethereum/evmone/releases>`_
library, others require `libz3 <https://github.com/Z3Prover/z3>`_. The test script
tries to discover the location of the ``evmone`` library, which can be located
in the current directory, installed on the system level, or the ``deps`` folder
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.
For running all compiler tests you may want to optionally install a few
dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_,
`libz3 <https://github.com/Z3Prover/z3>`_, and
`libhera <https://github.com/ewasm/hera>`_).
On macOS some of the testing scripts expect GNU coreutils to be installed.
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
compilation tests.
The test system automatically tries to discover the location of the ``evmone`` library
starting from the current directory. The required file is called ``libevmone.so`` on Linux systems,
``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``,
``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.8.0>`_
and place it in the project root path or inside the ``deps`` folder.
The test system automatically tries to discover the location of
the `evmone <https://github.com/ethereum/evmone/releases>`_ for running the semantic tests.
The ``evmone`` library must be located in the ``deps`` or ``deps/lib`` directory relative to the
current working directory, to its parent or its parent's parent. Alternatively an explicit location
for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environment variable.
``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
SMT tests by exporting ``SMT_FLAGS=--no-smt`` before running ``./scripts/tests.sh`` or

View 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). |
+-------------------------+-----------------------------------------------------------------------+

View File

@ -11,7 +11,7 @@ sign and verify signatures, and setup the payment channel.
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 only needs to send cryptographically signed messages off-chain

View 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,
}

View File

@ -1,11 +1,6 @@
Solidity
========
.. image:: logo.svg
:width: 120px
:alt: Solidity logo
:align: center
Solidity is an object-oriented, high-level language for implementing smart
contracts. Smart contracts are programs which govern the behaviour of accounts
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.
When deploying contracts, you should use the latest released
version of Solidity. This is because breaking changes as well as
new features and bug fixes are introduced regularly. We currently use
version of Solidity. Apart from exceptional cases, only the latest version receives
`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>`_.
.. warning::

View File

@ -275,7 +275,7 @@ The following transformation steps are the main components:
- Common Subexpression Eliminator
- Expression Simplifier
- Redundant Assign Eliminator
- Full Function Inliner
- Full Inliner
Optimizer Steps
---------------
@ -297,7 +297,8 @@ on the individual steps and their sequence below.
- :ref:`for-loop-condition-into-body`.
- :ref:`for-loop-condition-out-of-body`.
- :ref:`for-loop-init-rewriter`.
- :ref:`functional-inliner`.
- :ref:`expression-inliner`.
- :ref:`full-inliner`.
- :ref:`function-grouper`.
- :ref:`function-hoister`.
- :ref:`function-specializer`.
@ -1082,9 +1083,9 @@ The actual removal of the function is performed by the Unused Pruner.
Function Inlining
-----------------
.. _functional-inliner:
.. _expression-inliner:
FunctionalInliner
ExpressionInliner
^^^^^^^^^^^^^^^^^
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.
.. _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
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

View File

@ -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;: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.

View File

@ -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
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
value is considered ``true`` by ``JUMPI`` instruction, we do not clean
the boolean values before they are used as the condition for

View File

@ -1,3 +1,6 @@
.. index: ir breaking 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.
- 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.
In the old code generator, each function parameter has a fixed slot on the stack. If the function
is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the
function parameter's value is visible in the next execution of the function.
In the old code generator, each function parameter and return variable has a fixed slot on the stack.
If the function is run multiple times because ``_;`` is used multiple times or used in a loop, then a
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.
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
@ -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
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 used to be:

View File

@ -92,6 +92,12 @@ Block and Transaction Properties
``msg.value`` can change for every **external** function call.
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::
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
unless you know what you are doing.

View File

@ -308,7 +308,18 @@ Input Description
// "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.
// "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": {

View File

@ -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.
.. _yul-verbatim:
verbatim
^^^^^^^^
@ -1263,6 +1265,7 @@ Abbreviation Full name
``a`` ``SSATransform``
``t`` ``StructuralSimplifier``
``u`` ``UnusedPruner``
``p`` ``UnusedFunctionParameterPruner``
``d`` ``VarDeclInitializer``
============ ===============================

View File

@ -48,11 +48,11 @@ using namespace solidity::evmasm;
using namespace solidity::langutil;
using namespace solidity::util;
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
AssemblyItem const& Assembly::append(AssemblyItem _i)
{
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
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())
m_items.back().setLocation(m_currentSourceLocation);
m_items.back().m_modifierDepth = m_currentModifierDepth;
@ -68,7 +68,7 @@ unsigned Assembly::codeSize(unsigned subTagSize) const
ret += i.second.size();
for (AssemblyItem const& i: m_items)
ret += i.bytesRequired(tagSize);
ret += i.bytesRequired(tagSize, Precision::Approximate);
if (numberEncodingSize(ret) <= tagSize)
return static_cast<unsigned>(ret);
}
@ -96,13 +96,13 @@ public:
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)
{
flush();
m_location = _item.location();
printLocation();
printLocation(_debugInfoSelection);
}
string expression = _item.toAssemblyText(m_assembly);
@ -142,16 +142,29 @@ public:
m_pending.clear();
}
void printLocation()
void printLocation(DebugInfoSelection const& _debugInfoSelection)
{
if (!m_location.isValid())
if (!m_location.isValid() || (!_debugInfoSelection.location && !_debugInfoSelection.snippet))
return;
m_out << m_prefix << " /*";
if (m_location.sourceName)
m_out << " " + escapeAndQuoteString(*m_location.sourceName);
if (m_location.hasText())
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
m_out << " " << locationFromSources(m_sourceCodes, m_location);
if (_debugInfoSelection.location)
{
if (m_location.sourceName)
m_out << " " + escapeAndQuoteString(*m_location.sourceName);
if (m_location.hasText())
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
}
if (_debugInfoSelection.snippet)
{
if (_debugInfoSelection.location)
m_out << " ";
m_out << locationFromSources(m_sourceCodes, m_location);
}
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);
for (auto const& i: m_items)
f.feed(i);
f.feed(i, _debugInfoSelection);
f.flush();
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)
{
_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;
}
}
@ -194,10 +212,13 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co
_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;
assemblyStream(tmp, "", _sourceCodes);
assemblyStream(tmp, _debugInfoSelection, "", _sourceCodes);
return tmp.str();
}
@ -675,8 +696,11 @@ LinkerObject const& Assembly::assemble() const
break;
case PushImmutable:
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());
// Record the bytecode offset of the PUSH32 argument.
ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size());
// Advance bytecode by 32 bytes (default initialized).
ret.bytecode.resize(ret.bytecode.size() + 32);
break;
case VerbatimBytecode:
@ -684,6 +708,7 @@ LinkerObject const& Assembly::assemble() const
break;
case AssignImmutable:
{
// Expect 2 elements on stack (source, dest_base)
auto const& offsets = immutableReferencesBySub[i.data()].second;
for (size_t i = 0; i < offsets.size(); ++i)
{

View File

@ -24,6 +24,7 @@
#include <libevmasm/LinkerObject.h>
#include <libevmasm/Exceptions.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/EVMVersion.h>
#include <libsolutil/Common.h>
@ -64,7 +65,7 @@ public:
AssemblyItem newPushImmutable(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)); }
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
@ -142,10 +143,12 @@ public:
/// Create a text representation of the assembly.
std::string assemblyString(
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
StringMap const& _sourceCodes = StringMap()
) const;
void assemblyStream(
std::ostream& _out,
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
std::string const& _prefix = "",
StringMap const& _sourceCodes = StringMap()
) const;

View File

@ -65,7 +65,7 @@ void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
setData(data);
}
size_t AssemblyItem::bytesRequired(size_t _addressLength) const
size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) const
{
switch (m_type)
{
@ -87,10 +87,25 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength) const
case PushImmutable:
return 1 + 32;
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
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:
return std::get<2>(*m_verbatimBytecode).size();
default:
@ -322,6 +337,26 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
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(
AssemblyItems const& _items,
map<string, unsigned> const& _sourceIndicesMap
@ -334,6 +369,7 @@ std::string AssemblyItem::computeSourceMapping(
int prevSourceIndex = -1;
int prevModifierDepth = -1;
char prevJump = 0;
for (auto const& item: _items)
{
if (!ret.empty())
@ -402,6 +438,9 @@ std::string AssemblyItem::computeSourceMapping(
}
}
if (item.opcodeCount() > 1)
ret += string(item.opcodeCount() - 1, ';');
prevStart = location.start;
prevLength = length;
prevSourceIndex = sourceIndex;

View File

@ -27,6 +27,7 @@
#include <liblangutil/SourceLocation.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <optional>
#include <iostream>
#include <sstream>
@ -51,6 +52,8 @@ enum AssemblyItemType
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
};
enum class Precision { Precise , Approximate };
class Assembly;
class 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
/// 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 returnValues() const;
size_t deposit() const { return returnValues() - arguments(); }
@ -169,9 +175,11 @@ public:
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:
size_t opcodeCount() const noexcept;
AssemblyItemType m_type;
Instruction m_instruction; ///< 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.
mutable std::shared_ptr<u256> m_pushedValue;
/// 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;
for (AssemblyItem const& item: _items)
size += item.bytesRequired(_addressLength);
size += item.bytesRequired(_addressLength, _precision);
return size;
}

View File

@ -38,4 +38,4 @@ set(sources
)
add_library(evmasm ${sources})
target_link_libraries(evmasm PUBLIC solutil)
target_link_libraries(evmasm PUBLIC solutil fmt::fmt-header-only)

View File

@ -103,7 +103,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
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(

View File

@ -63,7 +63,7 @@ template<typename RangeType>
uint64_t codeSize(RangeType const& _itemRange)
{
return ranges::accumulate(_itemRange | ranges::views::transform(
[](auto const& _item) { return _item.bytesRequired(2); }
[](auto const& _item) { return _item.bytesRequired(2, Precision::Approximate); }
), 0u);
}
/// @returns the tag id, if @a _item is a PushTag or Tag into the current subassembly, nullopt otherwise.

View File

@ -388,6 +388,8 @@ size_t numberOfPops(AssemblyItems const& _items)
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)};
while (state.i < m_items.size())
applyMethods(
@ -398,7 +400,7 @@ bool PeepholeOptimiser::optimise()
);
if (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)
)
))

View File

@ -3,6 +3,8 @@ set(sources
Common.h
CharStream.cpp
CharStream.h
DebugInfoSelection.cpp
DebugInfoSelection.h
ErrorReporter.cpp
ErrorReporter.h
EVMVersion.h
@ -29,4 +31,4 @@ set(sources
)
add_library(langutil ${sources})
target_link_libraries(langutil PUBLIC solutil)
target_link_libraries(langutil PUBLIC solutil fmt::fmt-header-only)

View 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, ",");
}

View 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);
}

View File

@ -59,6 +59,13 @@ struct SourceLocation
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
{
if (!hasText() || !_other.hasText() || !equalSources(_other))

View File

@ -159,4 +159,4 @@ set(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)

View File

@ -533,14 +533,14 @@ void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall)
yul::ASTWalker::operator()(_functionCall);
if (auto const *builtinFunction = m_inlineAssembly->dialect().builtin(_functionCall.functionName.name))
if (builtinFunction->controlFlowSideEffects.terminates)
{
if (builtinFunction->controlFlowSideEffects.reverts)
connect(m_currentNode, m_revertNode);
else
connect(m_currentNode, m_transactionReturnNode);
{
if (builtinFunction->controlFlowSideEffects.canTerminate)
connect(m_currentNode, m_transactionReturnNode);
if (builtinFunction->controlFlowSideEffects.canRevert)
connect(m_currentNode, m_revertNode);
if (!builtinFunction->controlFlowSideEffects.canContinue)
m_currentNode = newLabel();
}
}
}
void ControlFlowBuilder::operator()(yul::FunctionDefinition const&)

View File

@ -25,7 +25,6 @@
#include <libsolutil/Algorithms.h>
#include <libsolutil/FunctionSelector.h>
#include <boost/range/adaptor/map.hpp>
#include <memory>
using namespace std;

View File

@ -246,7 +246,10 @@ public:
/// @returns the declared 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; }
bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; }
Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; }
bool isPublic() const { return visibility() >= Visibility::Public; }

View File

@ -41,7 +41,6 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <range/v3/view/enumerate.hpp>
#include <range/v3/view/reverse.hpp>
@ -2563,6 +2562,11 @@ string UserDefinedValueType::toString(bool /* _short */) const
return *definition().annotation().canonicalName;
}
string UserDefinedValueType::canonicalName() const
{
return *definition().annotation().canonicalName;
}
vector<tuple<string, Type const*>> UserDefinedValueType::makeStackItems() const
{
return underlyingType().stackItems();

View File

@ -1141,7 +1141,7 @@ public:
}
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, ""); }
protected:

View File

@ -135,11 +135,15 @@ string dispenseLocationComment(langutil::SourceLocation const& _location, IRGene
{
solAssert(_location.sourceName, "");
_context.markSourceUsed(*_location.sourceName);
return "/// " + AsmPrinter::formatSourceLocation(
string debugInfo = AsmPrinter::formatSourceLocation(
_location,
_context.sourceIndices(),
_context.debugInfoSelection(),
_context.soliditySourceProvider()
);
return debugInfo.empty() ? "" : "/// " + debugInfo;
}
string dispenseLocationComment(ASTNode const& _node, IRGenerationContext& _context)

View File

@ -30,6 +30,7 @@
#include <libsolidity/codegen/ir/Common.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/EVMVersion.h>
#include <libsolutil/Common.h>
@ -74,6 +75,7 @@ public:
RevertStrings _revertStrings,
OptimiserSettings _optimiserSettings,
std::map<std::string, unsigned> _sourceIndices,
langutil::DebugInfoSelection const& _debugInfoSelection,
langutil::CharStreamProvider const* _soliditySourceProvider
):
m_evmVersion(_evmVersion),
@ -81,6 +83,7 @@ public:
m_revertStrings(_revertStrings),
m_optimiserSettings(std::move(_optimiserSettings)),
m_sourceIndices(std::move(_sourceIndices)),
m_debugInfoSelection(_debugInfoSelection),
m_soliditySourceProvider(_soliditySourceProvider)
{}
@ -174,6 +177,7 @@ public:
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; }
private:
@ -220,6 +224,7 @@ private:
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
langutil::DebugInfoSelection m_debugInfoSelection = {};
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
};

View File

@ -95,7 +95,12 @@ pair<string, string> IRGenerator::run(
{
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))
{
string errorMessage;
@ -340,8 +345,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
return m_context.functionCollector().createFunction(functionName, [&]() {
m_context.resetLocalVariables();
Whiskers t(R"(
/// @ast-id <astID>
<sourceLocationComment>
<astIDComment><sourceLocationComment>
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<retInit>
<body>
@ -349,7 +353,10 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
<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(
"contractSourceLocationComment",
@ -409,8 +416,7 @@ string IRGenerator::generateModifier(
return m_context.functionCollector().createFunction(functionName, [&]() {
m_context.resetLocalVariables();
Whiskers t(R"(
/// @ast-id <astID>
<sourceLocationComment>
<astIDComment><sourceLocationComment>
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<assignRetParams>
<evalArgs>
@ -418,6 +424,7 @@ string IRGenerator::generateModifier(
}
<contractSourceLocationComment>
)");
t("functionName", functionName);
vector<string> retParamsIn;
for (auto const& varDecl: _function.returnParameters())
@ -440,7 +447,11 @@ string IRGenerator::generateModifier(
_modifierInvocation.name().annotation().referencedDeclaration
);
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(
"contractSourceLocationComment",
@ -546,14 +557,18 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
solAssert(paramTypes.empty(), "");
solUnimplementedAssert(type->sizeOnStack() == 1);
return Whiskers(R"(
/// @ast-id <astID>
<sourceLocationComment>
<astIDComment><sourceLocationComment>
function <functionName>() -> rval {
rval := loadimmutable("<id>")
}
<contractSourceLocationComment>
)")
("astID", to_string(_varDecl.id()))
(
"astIDComment",
m_context.debugInfoSelection().astID ?
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
""
)
("sourceLocationComment", dispenseLocationComment(_varDecl))
(
"contractSourceLocationComment",
@ -567,14 +582,18 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
{
solAssert(paramTypes.empty(), "");
return Whiskers(R"(
/// @ast-id <astID>
<sourceLocationComment>
<astIDComment><sourceLocationComment>
function <functionName>() -> <ret> {
<ret> := <constantValueFunction>()
}
<contractSourceLocationComment>
)")
("astID", to_string(_varDecl.id()))
(
"astIDComment",
m_context.debugInfoSelection().astID ?
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
""
)
("sourceLocationComment", dispenseLocationComment(_varDecl))
(
"contractSourceLocationComment",
@ -691,8 +710,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
}
return Whiskers(R"(
/// @ast-id <astID>
<sourceLocationComment>
<astIDComment><sourceLocationComment>
function <functionName>(<params>) -> <retVariables> {
<code>
}
@ -702,7 +720,12 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
("params", joinHumanReadable(parameters))
("retVariables", joinHumanReadable(returnVariables))
("code", std::move(code))
("astID", to_string(_varDecl.id()))
(
"astIDComment",
m_context.debugInfoSelection().astID ?
"/// @ast-id " + to_string(_varDecl.id()) + "\n" :
""
)
("sourceLocationComment", dispenseLocationComment(_varDecl))
(
"contractSourceLocationComment",
@ -829,7 +852,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract)
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
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");
else
t("astIDComment", "");
@ -944,7 +967,7 @@ string IRGenerator::callValueCheck()
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{
Whiskers t(R"X(
if iszero(lt(calldatasize(), 4))
<?+cases>if iszero(lt(calldatasize(), 4))
{
let selector := <shr224>(calldataload(0))
switch selector
@ -962,8 +985,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
}
</cases>
default {}
}
if iszero(calldatasize()) { <receiveEther> }
}</+cases>
<?+receiveEther>if iszero(calldatasize()) { <receiveEther> }</+receiveEther>
<fallback>
)X");
t("shr224", m_utils.shiftRightFunction(224));
@ -1085,6 +1108,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon
m_context.revertStrings(),
m_optimiserSettings,
m_context.sourceIndices(),
m_context.debugInfoSelection(),
m_context.soliditySourceProvider()
);
newContext.copyFunctionIDsFrom(m_context);

View File

@ -49,6 +49,7 @@ public:
RevertStrings _revertStrings,
OptimiserSettings _optimiserSettings,
std::map<std::string, unsigned> _sourceIndices,
langutil::DebugInfoSelection const& _debugInfoSelection,
langutil::CharStreamProvider const* _soliditySourceProvider
):
m_evmVersion(_evmVersion),
@ -59,6 +60,7 @@ public:
_revertStrings,
std::move(_optimiserSettings),
std::move(_sourceIndices),
_debugInfoSelection,
_soliditySourceProvider
),
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector())

View File

@ -991,7 +991,7 @@ void CHC::resetSourceAnalysis()
if (usesZ3)
{
/// 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());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());

View File

@ -201,7 +201,7 @@ void CompilerStack::findAndReportCyclicContractDependencies()
void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings)
{
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)
solAssert(!remapping.prefix.empty(), "");
m_importRemapper.setRemappings(move(_remappings));
@ -210,28 +210,28 @@ void CompilerStack::setRemappings(vector<ImportRemapper::Remapping> _remappings)
void CompilerStack::setViaIR(bool _viaIR)
{
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;
}
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
{
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;
}
void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
{
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;
}
void CompilerStack::setLibraries(std::map<std::string, util::h160> const& _libraries)
{
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;
}
@ -245,14 +245,14 @@ void CompilerStack::setOptimiserSettings(bool _optimize, size_t _runs)
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
{
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);
}
void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
{
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);
m_revertStrings = _revertStrings;
}
@ -260,21 +260,28 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings)
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
{
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;
}
void CompilerStack::setMetadataHash(MetadataHash _metadataHash)
{
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;
}
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)
{
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;
}
@ -310,9 +317,9 @@ void CompilerStack::reset(bool _keepSettings)
void CompilerStack::setSources(StringMap _sources)
{
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)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set sources before parsing."));
solThrow(CompilerError, "Must set sources before parsing.");
for (auto source: _sources)
m_sources[source.first].charStream = make_unique<CharStream>(/*content*/std::move(source.second), /*name*/source.first);
m_stackState = SourcesSet;
@ -321,7 +328,7 @@ void CompilerStack::setSources(StringMap _sources)
bool CompilerStack::parse()
{
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();
if (SemVerVersion{string(VersionString)}.isPrerelease())
@ -369,7 +376,7 @@ bool CompilerStack::parse()
void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
{
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;
map<string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(m_sourceJsons);
for (auto& src: reconstructedSources)
@ -392,7 +399,7 @@ void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
bool CompilerStack::analyze()
{
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();
for (Source const* source: m_sourceOrder)
@ -619,7 +626,7 @@ bool CompilerStack::compile(State _stopAfter)
return true;
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.
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
@ -691,7 +698,7 @@ void CompilerStack::link()
vector<string> CompilerStack::contractNames() const
{
if (m_stackState < Parsed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
solThrow(CompilerError, "Parsing was not successful.");
vector<string> contractNames;
for (auto const& contract: m_contracts)
contractNames.push_back(contract.first);
@ -701,7 +708,7 @@ vector<string> CompilerStack::contractNames() const
string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
{
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
string contractName;
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
{
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);
return currentContract.evmAssembly ? &currentContract.evmAssembly->items() : nullptr;
@ -723,7 +730,7 @@ evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contrac
evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
{
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);
return currentContract.evmRuntimeAssembly ? &currentContract.evmRuntimeAssembly->items() : nullptr;
@ -732,7 +739,7 @@ evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _
Json::Value CompilerStack::generatedSources(string const& _contractName, bool _runtime) const
{
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);
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
{
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);
if (!c.sourceMapping)
@ -789,7 +796,7 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
{
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);
if (!c.runtimeSourceMapping)
@ -805,7 +812,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
{
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)
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
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
return contract(_contractName).yulIR;
}
@ -837,7 +844,7 @@ string const& CompilerStack::yulIR(string const& _contractName) const
string const& CompilerStack::yulIROptimized(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
return contract(_contractName).yulIROptimized;
}
@ -845,7 +852,7 @@ string const& CompilerStack::yulIROptimized(string const& _contractName) const
string const& CompilerStack::ewasm(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
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
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
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
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
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
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
solThrow(CompilerError, "Compilation was not successful.");
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
{
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);
if (currentContract.evmAssembly)
return currentContract.evmAssembly->assemblyString(_sourceCodes);
return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes);
else
return string();
}
@ -891,7 +898,7 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap cons
Json::Value CompilerStack::assemblyJSON(string const& _contractName) const
{
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);
if (currentContract.evmAssembly)
@ -922,7 +929,7 @@ map<string, unsigned> CompilerStack::sourceIndices() const
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
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
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, "");
@ -994,7 +1001,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
{
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);
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
{
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);
}
@ -1013,7 +1020,7 @@ bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) cons
string const& CompilerStack::metadata(Contract const& _contract) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, "");
@ -1023,7 +1030,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const
CharStream const& CompilerStack::charStream(string const& _sourceName) const
{
if (m_stackState < SourcesSet)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set."));
solThrow(CompilerError, "No sources set.");
solAssert(source(_sourceName).charStream, "");
@ -1033,9 +1040,9 @@ CharStream const& CompilerStack::charStream(string const& _sourceName) const
SourceUnit const& CompilerStack::ast(string const& _sourceName) const
{
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)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
solThrow(CompilerError, "Parsing was not successful.");
return *source(_sourceName).ast;
}
@ -1043,7 +1050,7 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solThrow(CompilerError, "Analysis was not successful.");
return *contract(_contractName).contract;
}
@ -1054,7 +1061,7 @@ size_t CompilerStack::functionEntryPoint(
) const
{
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)
if (data.sourceID == _function.id())
@ -1256,7 +1263,7 @@ void CompilerStack::compileContract(
solAssert(!m_viaIR, "");
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
solThrow(CompilerError, "Called compile with errors.");
if (_otherCompilers.count(&_contract))
return;
@ -1294,7 +1301,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
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());
if (!compiledContract.yulIR.empty())
@ -1319,7 +1326,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
for (auto const& pair: m_contracts)
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(
_contract,
createCBORMetadata(compiledContract, /* _forIR */ true),
@ -1331,7 +1338,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEVMFromIR with errors."));
solThrow(CompilerError, "Called generateEVMFromIR with errors.");
if (!_contract.canBeDeployed())
return;
@ -1342,7 +1349,12 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
return;
// 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.optimize();
@ -1358,7 +1370,7 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
{
solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEwasm with errors."));
solThrow(CompilerError, "Called generateEwasm with errors.");
if (!_contract.canBeDeployed())
return;
@ -1369,7 +1381,12 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
return;
// 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.optimize();
@ -1412,14 +1429,14 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa
}
// 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
{
auto it = m_sources.find(_sourceName);
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;
}
@ -1658,7 +1675,7 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
{
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))
return Json::Value();

View File

@ -35,10 +35,11 @@
#include <libsmtutil/SolverInterface.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/SourceLocation.h>
#include <liblangutil/CharStreamProvider.h>
#include <libevmasm/LinkerObject.h>
@ -203,6 +204,9 @@ public:
/// @param _metadataHash can be IPFS, Bzzr1, None
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.
void setSources(StringMap _sources);
@ -505,6 +509,7 @@ private:
langutil::ErrorReporter m_errorReporter;
bool m_metadataLiteralSources = false;
MetadataHash m_metadataHash = MetadataHash::IPFS;
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
bool m_parserErrorRecovery = false;
State m_stackState = Empty;
bool m_importedSources = false;

View File

@ -104,10 +104,7 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
try
{
if (_kind != ReadCallback::kindString(ReadCallback::Kind::ReadFile))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment(
"ReadFile callback used as callback kind " +
_kind
));
solAssert(false, "ReadFile callback used as callback kind " + _kind);
string strippedSourceUnitName = _sourceUnitName;
if (strippedSourceUnitName.find("file://") == 0)
strippedSourceUnitName.erase(0, 7);
@ -171,7 +168,7 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so
}
catch (...)
{
return ReadCallback::Result{false, "Unknown exception in read callback."};
return ReadCallback::Result{false, "Unknown exception in read callback: " + boost::current_exception_diagnostic_information()};
}
}

View File

@ -44,20 +44,20 @@ struct OptimiserSettings
static char constexpr DefaultYulOptimiserSteps[] =
"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
"Lcul" // Simplify again
"Vcul jj" // Reverse SSA
"Vcul [j]" // Reverse SSA
// should have good "compilability" property here.
"Tpeul" // Run functional expression inliner
"xarulrul" // Prune a bit more in SSA
"xarrcL" // Turn into SSA again and simplify
"xa[rul]" // Prune a bit more in SSA
"xa[r]cL" // Turn into SSA again and simplify
"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.
static OptimiserSettings none()

View File

@ -28,9 +28,13 @@
#include <libyul/AssemblyStack.h>
#include <libyul/Exceptions.h>
#include <libyul/optimiser/Suite.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h>
#include <libsmtutil/Exceptions.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <libsolutil/JSON.h>
#include <libsolutil/Keccak256.h>
#include <libsolutil/CommonData.h>
@ -793,7 +797,7 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
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;
if (settings["debug"].isMember("revertStrings"))
@ -810,6 +814,31 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
);
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())
@ -1029,6 +1058,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.setRemappings(move(_inputsAndSettings.remappings));
compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);
if (_inputsAndSettings.debugInfoSelection.has_value())
compilerStack.selectDebugInfo(_inputsAndSettings.debugInfoSelection.value());
compilerStack.setLibraries(_inputsAndSettings.libraries);
compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources);
compilerStack.setMetadataHash(_inputsAndSettings.metadataHash);
@ -1151,13 +1182,13 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
"Exception during compilation: " + boost::diagnostic_information(_exception)
));
}
catch (std::exception const& _e)
catch (std::exception const& _exception)
{
errors.append(formatError(
Error::Severity::Error,
"Exception",
"general",
"Unknown exception during compilation" + (_e.what() ? ": " + string(_e.what()) : ".")
"Unknown exception during compilation: " + boost::diagnostic_information(_exception)
));
}
catch (...)
@ -1166,7 +1197,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
Error::Severity::Error,
"Exception",
"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(
_inputsAndSettings.evmVersion,
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& sourceContents = _inputsAndSettings.sources.begin()->second;
@ -1481,7 +1515,7 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
}
catch (...)
{
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile");
return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compile: " + boost::current_exception_diagnostic_information());
}
}

View File

@ -26,6 +26,8 @@
#include <libsolidity/interface/CompilerStack.h>
#include <libsolutil/JSON.h>
#include <liblangutil/DebugInfoSelection.h>
#include <optional>
#include <utility>
#include <variant>
@ -78,6 +80,7 @@ private:
std::vector<ImportRemapper::Remapping> remappings;
RevertStrings revertStrings = RevertStrings::Default;
OptimiserSettings optimiserSettings = OptimiserSettings::minimal();
std::optional<langutil::DebugInfoSelection> debugInfoSelection;
std::map<std::string, util::h160> libraries;
bool metadataLiteralSources = false;
CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS;

View File

@ -61,14 +61,9 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri
do \
{ \
if (!(_condition)) \
::boost::throw_exception( \
_exceptionType() << \
::solidity::util::errinfo_comment( \
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
) << \
::boost::throw_function(ETH_FUNC) << \
::boost::throw_file(__FILE__) << \
::boost::throw_line(__LINE__) \
solThrow( \
_exceptionType, \
::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \
); \
} \
while (false)

View File

@ -43,6 +43,19 @@ struct Exception: virtual std::exception, virtual boost::exception
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; } }
DEV_SIMPLE_EXCEPTION(InvalidAddress);

View File

@ -189,11 +189,13 @@ string Whiskers::replace(
if (conditionName[0] == '+')
{
string tag = conditionName.substr(1);
assertThrow(
_parameters.count(tag),
WhiskersError, "Tag " + tag + " used as condition but was not set."
);
conditionValue = !_parameters.at(tag).empty();
if (_parameters.count(tag))
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
{

View File

@ -60,12 +60,13 @@ DEV_SIMPLE_EXCEPTION(WhiskersError);
* - Condition parameter: <?name>...<!name>...</name>, where "<!name>" is optional
* 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
* - 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>
* 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.
* - 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
{

View File

@ -37,6 +37,8 @@
#include <boost/algorithm/string.hpp>
#include <fmt/format.h>
#include <memory>
#include <functional>
@ -618,7 +620,7 @@ void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _locati
m_errorReporter.typeError(
5473_error,
_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(
3781_error,
_location,
"Expected a value of type \"" +
_expectedType.str() +
"\" but got \"" +
_givenType.str() +
"\""
fmt::format("Expected a value of type \"{}\" but got \"{}\".", _expectedType, _givenType)
);
}
@ -664,14 +662,12 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
m_errorReporter.typeError(
_errorId,
_location,
"The \"" +
boost::to_lower_copy(instructionInfo(_instr).name)
+ "\" instruction is " +
vmKindMessage +
" VMs " +
"(you are currently compiling for \"" +
m_evmVersion.name() +
"\")."
fmt::format(
"The \"{instruction}\" instruction is {kind} VMs (you are currently compiling for \"{version}\").",
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr).name)),
fmt::arg("kind", vmKindMessage),
fmt::arg("version", m_evmVersion.name())
)
);
};

View File

@ -261,10 +261,16 @@ string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const
string AsmPrinter::formatSourceLocation(
SourceLocation const& _location,
map<string, unsigned> const& _nameToSourceIndex,
DebugInfoSelection const& _debugInfoSelection,
CharStreamProvider const* _soliditySourceProvider
)
{
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 solidityCodeSnippet = "";
@ -272,7 +278,7 @@ string AsmPrinter::formatSourceLocation(
{
sourceIndex = to_string(_nameToSourceIndex.at(*_location.sourceName));
if (_soliditySourceProvider)
if (_debugInfoSelection.snippet && _soliditySourceProvider)
{
solidityCodeSnippet = escapeAndQuoteString(
_soliditySourceProvider->charStream(*_location.sourceName).singleLineSnippet(_location)
@ -298,12 +304,13 @@ string AsmPrinter::formatSourceLocation(
string AsmPrinter::formatDebugData(shared_ptr<DebugData const> const& _debugData, bool _statement)
{
if (!_debugData)
if (!_debugData || m_debugInfoSelection.none())
return "";
vector<string> items;
if (auto id = _debugData->astID)
items.emplace_back("@ast-id " + to_string(*id));
if (m_debugInfoSelection.astID)
items.emplace_back("@ast-id " + to_string(*id));
if (
m_lastLocation != _debugData->originLocation &&
@ -315,6 +322,7 @@ string AsmPrinter::formatDebugData(shared_ptr<DebugData const> const& _debugData
items.emplace_back(formatSourceLocation(
_debugData->originLocation,
m_nameToSourceIndex,
m_debugInfoSelection,
m_soliditySourceProvider
));
}

View File

@ -29,6 +29,7 @@
#include <libsolutil/CommonData.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/SourceLocation.h>
#include <map>
@ -48,9 +49,11 @@ public:
explicit AsmPrinter(
Dialect const* _dialect = nullptr,
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
):
m_dialect(_dialect),
m_debugInfoSelection(_debugInfoSelection),
m_soliditySourceProvider(_soliditySourceProvider)
{
if (_sourceIndexToName)
@ -58,12 +61,12 @@ public:
m_nameToSourceIndex[*name] = index;
}
explicit AsmPrinter(
Dialect const& _dialect,
std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> _sourceIndexToName = {},
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
): AsmPrinter(&_dialect, _sourceIndexToName, _soliditySourceProvider) {}
): AsmPrinter(&_dialect, _sourceIndexToName, _debugInfoSelection, _soliditySourceProvider) {}
std::string operator()(Literal const& _literal);
std::string operator()(Identifier const& _identifier);
@ -83,6 +86,7 @@ public:
static std::string formatSourceLocation(
langutil::SourceLocation const& _location,
std::map<std::string, unsigned> const& _nameToSourceIndex,
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr
);
@ -100,6 +104,7 @@ private:
Dialect const* const m_dialect = nullptr;
std::map<std::string, unsigned> m_nameToSourceIndex;
langutil::SourceLocation m_lastLocation = {};
langutil::DebugInfoSelection m_debugInfoSelection = {};
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
};

View File

@ -249,7 +249,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
MachineAssemblyObject creationObject;
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
yulAssert(creationObject.bytecode->immutableReferences.empty(), "Leftover immutables.");
creationObject.assembly = creationAssembly->assemblyString();
creationObject.assembly = creationAssembly->assemblyString(m_debugInfoSelection);
creationObject.sourceMappings = make_unique<string>(
evmasm::AssemblyItem::computeSourceMapping(
creationAssembly->items(),
@ -261,7 +261,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
if (deployedAssembly)
{
deployedObject.bytecode = make_shared<evmasm::LinkerObject>(deployedAssembly->assemble());
deployedObject.assembly = deployedAssembly->assemblyString();
deployedObject.assembly = deployedAssembly->assemblyString(m_debugInfoSelection);
deployedObject.sourceMappings = make_unique<string>(
evmasm::AssemblyItem::computeSourceMapping(
deployedAssembly->items(),
@ -314,11 +314,13 @@ AssemblyStack::assembleEVMWithDeployed(optional<string_view> _deployName) const
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->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

View File

@ -22,9 +22,10 @@
#pragma once
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/CharStreamProvider.h>
#include <libyul/Object.h>
#include <libyul/ObjectParser.h>
@ -69,12 +70,24 @@ public:
enum class Machine { EVM, Ewasm };
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_evmVersion(_evmVersion),
m_optimiserSettings(std::move(_optimiserSettings)),
m_debugInfoSelection(_debugInfoSelection),
m_errorReporter(m_errors)
{}
@ -116,7 +129,9 @@ public:
langutil::ErrorList const& errors() const { return m_errors; }
/// 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.
std::shared_ptr<Object> parserResult() const;
@ -132,6 +147,7 @@ private:
Language m_language = Language::Assembly;
langutil::EVMVersion m_evmVersion;
solidity::frontend::OptimiserSettings m_optimiserSettings;
langutil::DebugInfoSelection m_debugInfoSelection{};
std::unique_ptr<langutil::CharStream> m_charStream;

View File

@ -34,6 +34,9 @@ add_library(yul
AssemblyStack.cpp
CompilabilityChecker.cpp
CompilabilityChecker.h
ControlFlowSideEffects.h
ControlFlowSideEffectsCollector.cpp
ControlFlowSideEffectsCollector.h
Dialect.cpp
Dialect.h
Exceptions.h
@ -216,4 +219,4 @@ add_library(yul
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)

View File

@ -18,22 +18,32 @@
#pragma once
#include <set>
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
{
/// If true, this code terminates the control flow.
/// State may or may not be reverted as indicated by the ``reverts`` flag.
bool terminates = false;
/// If true, this code reverts all state changes in the transaction.
/// Whenever this is true, ``terminates`` has to be true as well.
bool reverts = false;
/// If true, the function contains at least one reachable branch that terminates successfully.
bool canTerminate = false;
/// If true, the function contains at least one reachable branch that reverts.
bool canRevert = false;
/// If true, the function has a regular outgoing control-flow.
bool canContinue = true;
bool terminatesOrReverts() const
{
return (canTerminate || canRevert) && !canContinue;
}
};
}

View 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);
}

View 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;
};
}

View File

@ -22,8 +22,8 @@
#pragma once
#include <libyul/YulString.h>
#include <libyul/SideEffects.h>
#include <libyul/ControlFlowSideEffects.h>
#include <libyul/SideEffects.h>
#include <vector>
#include <set>

View File

@ -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) + "\"";
}
string Object::toString(
Dialect const* _dialect,
DebugInfoSelection const& _debugInfoSelection,
CharStreamProvider const* _soliditySourceProvider
) const
{
@ -74,10 +75,15 @@ string Object::toString(
})) +
"\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)
inner += "\n" + obj->toString(_dialect, _soliditySourceProvider);
inner += "\n" + obj->toString(_dialect, _debugInfoSelection, _soliditySourceProvider);
return useSrcComment + "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
}

View File

@ -25,6 +25,7 @@
#include <libyul/YulString.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/DebugInfoSelection.h>
#include <libsolutil/Common.h>
@ -54,6 +55,7 @@ struct ObjectNode
YulString name;
virtual std::string toString(
Dialect const* _dialect,
langutil::DebugInfoSelection const& _debugInfoSelection,
langutil::CharStreamProvider const* _soliditySourceProvider
) const = 0;
};
@ -69,6 +71,7 @@ struct Data: public ObjectNode
std::string toString(
Dialect const* _dialect,
langutil::DebugInfoSelection const& _debugInfoSelection,
langutil::CharStreamProvider const* _soliditySourceProvider
) const override;
};
@ -89,6 +92,7 @@ public:
/// @returns a (parseable) string representation.
std::string toString(
Dialect const* _dialect,
langutil::DebugInfoSelection const& _debugInfoSelection = langutil::DebugInfoSelection::Default(),
langutil::CharStreamProvider const* _soliditySourceProvider = nullptr
) const;

View File

@ -94,7 +94,7 @@ struct SideEffects
cannotLoop && _other.cannotLoop,
otherState + _other.otherState,
storage + _other.storage,
memory + _other.memory
memory + _other.memory
};
}

View File

@ -21,6 +21,8 @@
#pragma once
#include <fmt/format.h>
#include <unordered_map>
#include <memory>
#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
{
template<> struct hash<solidity::yul::YulString>

View File

@ -23,7 +23,6 @@
#include <libyul/AST.h>
#include <libyul/Exceptions.h>
#include <libyul/Utilities.h>
#include <libyul/AsmPrinter.h>
#include <libsolutil/cxx20.h>
#include <libsolutil/Visitor.h>
@ -230,7 +229,7 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
// not only for builtins.
if (auto const* funCall = get_if<FunctionCall>(&_exprStmt.expression))
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 = &m_graph.makeBlock(debugDataOf(*m_currentBlock));

View File

@ -57,8 +57,20 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
f.parameters.resize(static_cast<size_t>(info.args));
f.returns.resize(static_cast<size_t>(info.ret));
f.sideEffects = EVMDialect::sideEffectsOfInstruction(_instruction);
f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction);
f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction);
if (evmasm::SemanticInformation::terminatesControlFlow(_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.literalArguments.clear();
f.instruction = _instruction;

View File

@ -484,7 +484,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
yulAssert(!_block.operations.empty(), "");
CFG::BuiltinCall const* builtinCall = get_if<CFG::BuiltinCall>(&_block.operations.back().operation);
yulAssert(builtinCall, "");
yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminates, "");
yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminatesOrReverts(), "");
}
}, _block.exit);
// TODO: We could assert that the last emitted assembly item terminated or was an (unconditional) jump.

View File

@ -129,8 +129,9 @@ WasmDialect::WasmDialect()
m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None;
m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None;
m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None;
m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true;
m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canTerminate = false;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canRevert = true;
m_functions["unreachable"_yulstring].controlFlowSideEffects.canContinue = false;
addFunction("datasize", {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", "getBlockNumber", {}, {i64}},
{"eth", "getTxOrigin", {i32ptr}, {}},
{"eth", "finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false}},
{"eth", "revert", {i32ptr, i32}, {}, ControlFlowSideEffects{true, true}},
{"eth", "finish", {i32ptr, i32}, {}, ControlFlowSideEffects{true, false, false}},
{"eth", "revert", {i32ptr, i32}, {}, ControlFlowSideEffects{false, true, false}},
{"eth", "getReturnDataSize", {}, {i32}},
{"eth", "returnDataCopy", {i32ptr, i32, i32}, {}},
{"eth", "selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{true, false}},
{"eth", "selfDestruct", {i32ptr}, {}, ControlFlowSideEffects{false, true, false}},
{"eth", "getBlockTimestamp", {}, {i64}},
{"debug", "print32", {i32}, {}},
{"debug", "print64", {i64}, {}},
@ -240,7 +241,7 @@ void WasmDialect::addExternals()
// TODO some of them are side effect free.
f.sideEffects = SideEffects::worst();
f.sideEffects.cannotLoop = true;
f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminates;
f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminatesOrReverts();
f.controlFlowSideEffects = ext.controlFlowSideEffects;
f.isMSize = false;
f.literalArguments.clear();

View File

@ -22,7 +22,7 @@ using namespace std;
using namespace solidity;
using namespace solidity::yul;
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block& _block)
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block const& _block)
{
FunctionDefinitionCollector functionDefinitionCollector;
functionDefinitionCollector(_block);

View File

@ -34,7 +34,7 @@ namespace solidity::yul
class FunctionDefinitionCollector: ASTWalker
{
public:
static std::map<YulString, FunctionDefinition const*> run(Block& _block);
static std::map<YulString, FunctionDefinition const*> run(Block const& _block);
private:
using ASTWalker::operator();
void operator()(FunctionDefinition const& _functionDefinition) override;

View File

@ -78,6 +78,9 @@
#include <range/v3/view/map.hpp>
#include <range/v3/action/remove.hpp>
#include <limits>
#include <tuple>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
@ -87,7 +90,7 @@ void OptimiserSuite::run(
GasMeter const* _meter,
Object& _object,
bool _optimizeStackAllocation,
string const& _optimisationSequence,
string_view _optimisationSequence,
optional<size_t> _expectedExecutionsPerDeployment,
set<YulString> const& _externallyUsedIdentifiers
)
@ -267,9 +270,9 @@ map<char, string> const& OptimiserSuite::stepAbbreviationToNameMap()
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)
switch (abbreviation)
{
@ -277,12 +280,12 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations)
case '\n':
break;
case '[':
assertThrow(!insideLoop, OptimizerException, "Nested brackets are not supported");
insideLoop = true;
assertThrow(nestingLevel < numeric_limits<int8_t>::max(), OptimizerException, "Brackets nested too deep");
nestingLevel++;
break;
case ']':
assertThrow(insideLoop, OptimizerException, "Unbalanced brackets");
insideLoop = false;
nestingLevel--;
assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets");
break;
default:
{
@ -301,43 +304,99 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations)
OptimizerException,
"'"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);
string input = _stepAbbreviations;
ranges::actions::remove(input, ' ');
ranges::actions::remove(input, '\n');
// This splits 'aaa[bbb]ccc...' into 'aaa' and '[bbb]ccc...'.
auto extractNonNestedPrefix = [](string_view _tail) -> tuple<string_view, string_view>
{
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;
for (char abbreviation: _sequence)
steps.emplace_back(stepAbbreviationToNameMap().at(abbreviation));
if (abbreviation != ' ' && abbreviation != '\n')
steps.emplace_back(stepAbbreviationToNameMap().at(abbreviation));
return steps;
};
// The sequence has now been validated and must consist of pairs of segments that look like this: `aaa[bbb]`
// `aaa` or `[bbb]` can be empty. For example we consider a sequence like `fgo[aaf]Oo` to have
// four segments, the last of which is an empty bracket.
size_t currentPairStart = 0;
while (currentPairStart < input.size())
vector<tuple<string_view, bool>> subsequences;
string_view tail = _stepAbbreviations;
while (!tail.empty())
{
size_t openingBracket = input.find('[', currentPairStart);
size_t closingBracket = input.find(']', openingBracket);
size_t firstCharInside = (openingBracket == string::npos ? input.size() : openingBracket + 1);
yulAssert((openingBracket == string::npos) == (closingBracket == string::npos), "");
string_view subsequence;
tie(subsequence, tail) = extractNonNestedPrefix(tail);
if (subsequence.size() > 0)
subsequences.push_back({subsequence, false});
runSequence(abbreviationsToSteps(input.substr(currentPairStart, openingBracket - currentPairStart)), _ast);
runSequenceUntilStable(abbreviationsToSteps(input.substr(firstCharInside, closingBracket - firstCharInside)), _ast);
if (tail.empty())
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);
}
}

View File

@ -29,6 +29,7 @@
#include <set>
#include <string>
#include <string_view>
#include <memory>
namespace solidity::yul
@ -64,22 +65,17 @@ public:
GasMeter const* _meter,
Object& _object,
bool _optimizeStackAllocation,
std::string const& _optimisationSequence,
std::string_view _optimisationSequence,
std::optional<size_t> _expectedExecutionsPerDeployment,
std::set<YulString> const& _externallyUsedIdentifiers = {}
);
/// Ensures that specified sequence of step abbreviations is well-formed and can be executed.
/// @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::string const& _stepAbbreviations, Block& _ast);
void runSequenceUntilStable(
std::vector<std::string> const& _steps,
Block& _ast,
size_t maxRounds = MaxRounds
);
void runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable = false);
static std::map<std::string, std::unique_ptr<OptimiserStep>> const& allSteps();
static std::map<std::string, char> const& stepNameToAbbreviationMap();

View File

@ -413,7 +413,7 @@ def commandline_parser() -> ArgumentParser:
action='store_true',
help="Immediately exit and print compiler output if the compiler exits with an error.",
)
return parser;
return parser
if __name__ == "__main__":

View File

@ -20,7 +20,7 @@
# ------------------------------------------------------------------------------
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")
function compileFull()
{
@ -99,7 +99,7 @@ function compileFull()
printError "Inside directory:"
echo " $(pwd)"
printError "Input was:"
cat -- "${files[@]}"
echo "${files[@]}"
false
fi
}

View File

@ -39,7 +39,7 @@ def colorize(left, right, id):
reset = "\x1b[0m"
colors = [red, yellow]
color = colors[id % len(colors)]
function, arguments, results = parse_call(right)
function, _arguments, _results = parse_call(right)
left = left.replace("compileAndRun", color + "compileAndRun" + reset)
right = right.replace("constructor", color + "constructor" + reset)
if function:
@ -158,7 +158,7 @@ def main(argv):
interactive = False
input_file = None
try:
opts, args = getopt.getopt(argv, "if:")
opts, _args = getopt.getopt(argv, "if:")
except getopt.GetoptError:
print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
sys.exit(1)

View File

@ -158,7 +158,7 @@ class TraceAnalyser:
for trace_id, trace in enumerate(left.traces):
left_trace = trace
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):
mismatch_info = " " + str(left_trace) + "\n"
mismatch_info += " " + str(right_trace) + "\n"
@ -179,7 +179,7 @@ def main(argv):
extracted_tests_trace_file = None
end_to_end_trace_file = None
try:
opts, args = getopt.getopt(argv, "s:e:")
opts, _args = getopt.getopt(argv, "s:e:")
except getopt.GetoptError:
print("verify-testcases.py [-s <path to semantic-trace>] [-e <path to endToEndExtraction-trace>]")
sys.exit(2)

View File

@ -261,9 +261,9 @@ def main(argv):
no_confirm = False
examine_coverage = 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":
check = True
elif opt == "--fix":

View File

@ -49,9 +49,10 @@ def readDependencies(fname):
if line[0] == '\t':
library = line.split(' ', 1)[0][1:]
if (library.startswith("/usr/local/lib") or
library.startswith("/usr/local/opt") or
library.startswith("/Users/")):
if (os.path.basename(library) != os.path.basename(fname)):
library.startswith("/usr/local/opt") or
library.startswith("/Users/")
):
if os.path.basename(library) != os.path.basename(fname):
command = "install_name_tool -change " + \
library + " @executable_path/./" + \
os.path.basename(library) + " " + fname

View File

@ -20,7 +20,7 @@ repository. The changes are compared against ``origin/develop``.
import subprocess
from pathlib import Path
from enum import Enum
from parsec import *
from parsec import generate, ParseError, regex, string
from tabulate import tabulate
class Kind(Enum):
@ -56,10 +56,10 @@ def diff_string() -> (Kind, Diff, int):
-// gas irOptimized: 138070
"""
diff_kind = yield (minus | plus)
diff_kind = yield minus | plus
yield comment
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 space
val = yield number()

View File

@ -179,16 +179,6 @@ case $(uname -s) in
. /etc/os-release
install_z3=""
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)
#jessie
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 "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)
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).)"
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)
echo "Installing solidity dependencies."
install_z3="libz3-dev"
@ -311,6 +280,10 @@ case $(uname -s) in
echo "Installing solidity dependencies."
install_z3="libz3-dev"
;;
hirsute)
echo "Installing solidity dependencies."
install_z3="libz3-dev"
;;
betsy)
#do not try anything for betsy.
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 - This might not work, but we are trying anyway."
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"
;;
esac

View File

@ -6,7 +6,6 @@
# into files for e.g. fuzz testing as
# scripts/isolate_tests.py test/libsolidity/*
import sys
import re
import os
import hashlib

View File

@ -9,7 +9,6 @@ from os import path, walk
from sys import exit
from textwrap import dedent
import subprocess
import sys
PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__)))
PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc"

View File

@ -24,24 +24,15 @@ disable=
duplicate-code,
invalid-name,
missing-docstring,
mixed-indentation,
no-else-return,
no-self-use,
pointless-string-statement,
redefined-builtin,
redefined-outer-name,
singleton-comparison,
superfluous-parens,
too-few-public-methods,
trailing-newlines,
undefined-variable,
ungrouped-imports,
unnecessary-semicolon,
unused-import,
unused-variable,
unused-wildcard-import,
useless-object-inheritance,
wildcard-import
too-many-public-methods,
ungrouped-imports
[BASIC]

View File

@ -11,7 +11,7 @@ import time
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"""
def __init__(self, interval=300):
@ -30,7 +30,7 @@ class PrintDotsThread(object):
print(".")
time.sleep(self.interval)
class regressor():
class regressor:
_re_sanitizer_log = re.compile(r"""ERROR: (libFuzzer|UndefinedBehaviorSanitizer)""")
def __init__(self, description, args):

View File

@ -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"
mv solidity solc
# Fetch jsoncpp dependency
# Fetch dependencies
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/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
cd solc

View File

@ -44,9 +44,7 @@ def writeSourceToFile(lines):
os.system("mkdir -p " + filePath)
with open(srcName, mode='a+', encoding='utf8', newline='') as f:
createdSources.append(srcName)
i = 0
for idx, line in enumerate(lines[1:]):
# write to file
if line[:12] != "==== Source:":
f.write(line)

View File

@ -7,7 +7,8 @@ import sys
import re
import os
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):

View File

@ -41,9 +41,9 @@ parts:
source-tag: z3-4.8.4
plugin: make
build-packages: [python3]
stage-packages: [libstdc++6]
stage-packages: [libstdc++6, libgomp1]
override-build: |
python scripts/mk_make.py
python3 scripts/mk_make.py
cd build
make -j -l $(grep -c "^processor" /proc/cpuinfo)
make install DESTDIR=$SNAPCRAFT_PART_INSTALL

View File

@ -553,7 +553,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
if (fs::exists(pathName) && !m_options.output.overwriteFiles)
{
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
m_error = true;
m_outputFailed = true;
return;
}
ofstream outFile(pathName);
@ -561,7 +561,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
if (!outFile)
{
serr() << "Could not write to file \"" << pathName << "\"." << endl;
m_error = true;
m_outputFailed = true;
return;
}
}
@ -631,6 +631,8 @@ bool CommandLineInterface::compile()
m_compiler->setViaIR(m_options.output.experimentalViaIR);
m_compiler->setEVMVersion(m_options.output.evmVersion);
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
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");
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)
{
if (_error.type() == Error::Type::DocstringParsingError)
@ -735,23 +713,6 @@ bool CommandLineInterface::compile()
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;
}
@ -894,7 +855,7 @@ bool CommandLineInterface::actOnInput()
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
outputCompilationResults();
}
return !m_error;
return !m_outputFailed;
}
bool CommandLineInterface::link()
@ -976,6 +937,7 @@ void CommandLineInterface::writeLinkedFiles()
if (!outFile)
{
serr() << "Could not write to file " << src.first << ". Aborting." << endl;
m_outputFailed = true;
return;
}
}
@ -1013,34 +975,16 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
m_options.output.evmVersion,
_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))
successful = false;
else
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;
}
if (!stack.parseAndAnalyze(src.first, src.second))
successful = false;
else
stack.optimize();
}
for (auto const& sourceAndStack: assemblyStacks)
@ -1074,29 +1018,8 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
{
try
{
stack.translate(yul::AssemblyStack::Language::Ewasm);
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;
}
stack.translate(yul::AssemblyStack::Language::Ewasm);
stack.optimize();
sout() << endl << "==========================" << endl;
sout() << endl << "Translated source:" << endl;
@ -1104,28 +1027,8 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
}
yul::MachineAssemblyObject object;
try
{
object = stack.assemble(_targetMachine);
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;
}
object = stack.assemble(_targetMachine);
object.bytecode->link(m_options.linker.libraries);
sout() << endl << "Binary representation:" << endl;
if (object.bytecode)

View File

@ -121,7 +121,7 @@ private:
std::ostream& m_sout;
std::ostream& m_serr;
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;
std::optional<std::string> m_standardJsonInput;
std::unique_ptr<frontend::CompilerStack> m_compiler;

View File

@ -67,6 +67,7 @@ static string const g_strImportAst = "import-ast";
static string const g_strInputFile = "input-file";
static string const g_strYul = "yul";
static string const g_strYulDialect = "yul-dialect";
static string const g_strDebugInfo = "debug-info";
static string const g_strIPFS = "ipfs";
static string const g_strLicense = "license";
static string const g_strLibraries = "libraries";
@ -137,6 +138,14 @@ static set<string> const g_metadataHashArgs
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()
{
sout() <<
@ -244,6 +253,7 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
output.evmVersion == _other.output.evmVersion &&
output.experimentalViaIR == _other.output.experimentalViaIR &&
output.revertStrings == _other.output.revertStrings &&
output.debugInfoSelection == _other.output.debugInfoSelection &&
output.stopAfter == _other.output.stopAfter &&
input.mode == _other.input.mode &&
assembly.targetMachine == _other.assembly.targetMachine &&
@ -460,6 +470,47 @@ bool CommandLineParser::parseLibraryOption(string const& _input)
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()
{
// Declare the supported options.
@ -546,6 +597,13 @@ General Information)").c_str(),
po::value<string>()->value_name(joinHumanReadable(g_revertStringsArgs, ",")),
"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(),
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;
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)
@ -912,6 +976,23 @@ bool CommandLineParser::processArgs()
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())
return false;
@ -930,8 +1011,8 @@ bool CommandLineParser::processArgs()
m_options.formatting.json.indent = m_args[g_strJsonIndent].as<uint32_t>();
}
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0);
if (!parseOutputSelection())
return false;
m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0);

View File

@ -24,8 +24,12 @@
#include <libsolidity/interface/DebugSettings.h>
#include <libsolidity/interface/FileReader.h>
#include <libsolidity/interface/ImportRemapper.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/EVMVersion.h>
#include <libsolutil/JSON.h>
#include <boost/program_options.hpp>
@ -174,6 +178,7 @@ struct CommandLineOptions
langutil::EVMVersion evmVersion;
bool experimentalViaIR = false;
RevertStrings revertStrings = RevertStrings::Default;
std::optional<langutil::DebugInfoSelection> debugInfoSelection;
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
} output;
@ -286,6 +291,8 @@ private:
/// @return false if there are any validation errors, true otherwise.
bool parseLibraryOption(std::string const& _input);
bool parseOutputSelection();
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
[[noreturn]] void printVersionAndExit();
[[noreturn]] void printLicenseAndExit();

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