mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8193 from ethereum/develop
Merge develop into release for 0.6.2.
This commit is contained in:
commit
bacdbe5787
@ -295,7 +295,6 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
CC: clang
|
CC: clang
|
||||||
CXX: clang++
|
CXX: clang++
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *run_build
|
- run: *run_build
|
||||||
@ -305,8 +304,6 @@ jobs:
|
|||||||
b_ubu: &build_ubuntu1904
|
b_ubu: &build_ubuntu1904
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
|
||||||
environment:
|
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *run_build
|
- run: *run_build
|
||||||
@ -317,13 +314,12 @@ jobs:
|
|||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu1904
|
||||||
environment:
|
environment:
|
||||||
FORCE_RELEASE: ON
|
FORCE_RELEASE: ON
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
|
|
||||||
b_ubu18: &build_ubuntu1804
|
b_ubu18: &build_ubuntu1804
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >>
|
||||||
environment:
|
environment:
|
||||||
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 -DLLL=ON
|
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2
|
||||||
CMAKE_BUILD_TYPE: RelWithDebugInfo
|
CMAKE_BUILD_TYPE: RelWithDebugInfo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@ -368,7 +364,7 @@ jobs:
|
|||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu1904
|
||||||
environment:
|
environment:
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF -DLLL=ON
|
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *run_build
|
- run: *run_build
|
||||||
@ -408,7 +404,6 @@ jobs:
|
|||||||
- image: archlinux/base
|
- image: archlinux/base
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: Install build dependencies
|
name: Install build dependencies
|
||||||
@ -425,7 +420,6 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_OPTIONS: -DLLL=ON
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -10,7 +10,7 @@ include(EthPolicy)
|
|||||||
eth_policy()
|
eth_policy()
|
||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.6.1")
|
set(PROJECT_VERSION "0.6.2")
|
||||||
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
||||||
|
|
||||||
include(TestBigEndian)
|
include(TestBigEndian)
|
||||||
@ -19,10 +19,7 @@ if (IS_BIG_ENDIAN)
|
|||||||
message(FATAL_ERROR "${PROJECT_NAME} currently does not support big endian systems.")
|
message(FATAL_ERROR "${PROJECT_NAME} currently does not support big endian systems.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(LLL "Build LLL" OFF)
|
|
||||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||||
option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF)
|
|
||||||
option(INSTALL_LLLC "Include lllc executable in installation" ${LLL})
|
|
||||||
|
|
||||||
# Setup cccache.
|
# Setup cccache.
|
||||||
include(EthCcache)
|
include(EthCcache)
|
||||||
@ -52,7 +49,7 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens
|
|||||||
include(EthOptions)
|
include(EthOptions)
|
||||||
configure_project(TESTS)
|
configure_project(TESTS)
|
||||||
|
|
||||||
add_subdirectory(libdevcore)
|
add_subdirectory(libsolutil)
|
||||||
add_subdirectory(liblangutil)
|
add_subdirectory(liblangutil)
|
||||||
add_subdirectory(libevmasm)
|
add_subdirectory(libevmasm)
|
||||||
add_subdirectory(libyul)
|
add_subdirectory(libyul)
|
||||||
@ -61,10 +58,6 @@ add_subdirectory(libsolc)
|
|||||||
|
|
||||||
if (NOT EMSCRIPTEN)
|
if (NOT EMSCRIPTEN)
|
||||||
add_subdirectory(solc)
|
add_subdirectory(solc)
|
||||||
if (LLL)
|
|
||||||
add_subdirectory(liblll)
|
|
||||||
add_subdirectory(lllc)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TESTS AND NOT EMSCRIPTEN)
|
if (TESTS AND NOT EMSCRIPTEN)
|
||||||
|
@ -211,7 +211,7 @@ for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end();
|
|||||||
|
|
||||||
## 12. Include Headers
|
## 12. Include Headers
|
||||||
|
|
||||||
1. Includes should go in increasing order of generality (`libsolidity` -> `libevmasm` -> `libdevcore` -> `boost` -> `STL`).
|
1. Includes should go in increasing order of generality (`libsolidity` -> `libevmasm` -> `libsolutil` -> `boost` -> `STL`).
|
||||||
2. The corresponding `.h` file should be the first include in the respective `.cpp` file.
|
2. The corresponding `.h` file should be the first include in the respective `.cpp` file.
|
||||||
3. Insert empty lines between blocks of include files.
|
3. Insert empty lines between blocks of include files.
|
||||||
|
|
||||||
@ -226,8 +226,8 @@ Example:
|
|||||||
|
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/SHA3.h>
|
#include <libsolutil/SHA3.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
28
Changelog.md
28
Changelog.md
@ -1,3 +1,29 @@
|
|||||||
|
### 0.6.2 (2020-01-27)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Allow accessing external functions via contract and interface names to obtain their selector.
|
||||||
|
* Allow interfaces to inherit from other interfaces
|
||||||
|
* Allow gas and value to be set in external function calls using ``c.f{gas: 10000, value: 4 ether}()``.
|
||||||
|
* Allow specifying the ``salt`` for contract creations and thus the ``create2`` opcode using ``new C{salt: 0x1234, value: 1 ether}(arg1, arg2)``.
|
||||||
|
* Inline Assembly: Support literals ``true`` and ``false``.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
* LLL: The LLL compiler has been removed.
|
||||||
|
* General: Raise warning if runtime bytecode exceeds 24576 bytes (a limit introduced in Spurious Dragon).
|
||||||
|
* General: Support compiling starting from an imported AST. Among others, this can be used for mutation testing.
|
||||||
|
* Yul Optimizer: Apply penalty when trying to rematerialize into loops.
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Commandline interface: Only activate yul optimizer if ``--optimize`` is given.
|
||||||
|
* Fixes internal compiler error on explicitly calling unimplemented base functions.
|
||||||
|
|
||||||
|
|
||||||
|
Build System:
|
||||||
|
* Switch to building soljson.js with an embedded base64-encoded wasm binary.
|
||||||
|
|
||||||
|
|
||||||
### 0.6.1 (2020-01-02)
|
### 0.6.1 (2020-01-02)
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
@ -900,7 +926,7 @@ Features:
|
|||||||
* ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``,
|
* ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``,
|
||||||
``nonpayable`` and ``payable``.
|
``nonpayable`` and ``payable``.
|
||||||
* Analyzer: Experimental partial support for Z3 SMT checker ("SMTChecker").
|
* Analyzer: Experimental partial support for Z3 SMT checker ("SMTChecker").
|
||||||
* Build System: Shared libraries (``libdevcore``, ``libevmasm``, ``libsolidity``
|
* Build System: Shared libraries (``libsolutil``, ``libevmasm``, ``libsolidity``
|
||||||
and ``liblll``) are no longer produced during the build process.
|
and ``liblll``) are no longer produced during the build process.
|
||||||
* Code generator: Experimental new implementation of ABI encoder that can
|
* Code generator: Experimental new implementation of ABI encoder that can
|
||||||
encode arbitrarily nested arrays ("ABIEncoderV2")
|
encode arbitrarily nested arrays ("ABIEncoderV2")
|
||||||
|
@ -34,6 +34,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
add_compile_options(-Wall)
|
add_compile_options(-Wall)
|
||||||
add_compile_options(-Wextra)
|
add_compile_options(-Wextra)
|
||||||
add_compile_options(-Werror)
|
add_compile_options(-Werror)
|
||||||
|
add_compile_options(-pedantic)
|
||||||
|
add_compile_options(-Wno-unknown-pragmas)
|
||||||
|
add_compile_options(-Wimplicit-fallthrough)
|
||||||
|
|
||||||
# Configuration-specific compiler settings.
|
# Configuration-specific compiler settings.
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
|
||||||
@ -110,9 +113,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
|
||||||
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
|
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
|
||||||
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
|
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue']")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
|
||||||
# Do not build as a WebAssembly target - we need an asm.js output.
|
# Build for webassembly target.
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
|
||||||
|
# Set webassembly build to synchronous loading.
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM_ASYNC_COMPILATION=0")
|
||||||
|
# Output a single js file with the wasm binary embedded as base64 string.
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SINGLE_FILE=1")
|
||||||
|
|
||||||
# Disable warnings about not being pure asm.js due to memory growth.
|
# Disable warnings about not being pure asm.js due to memory growth.
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
|
||||||
|
@ -41,7 +41,6 @@ if (SUPPORT_TOOLS)
|
|||||||
endif()
|
endif()
|
||||||
message("------------------------------------------------------------------ flags")
|
message("------------------------------------------------------------------ flags")
|
||||||
message("-- OSSFUZZ ${OSSFUZZ}")
|
message("-- OSSFUZZ ${OSSFUZZ}")
|
||||||
message("-- LLL ${LLL}")
|
|
||||||
message("------------------------------------------------------------------------")
|
message("------------------------------------------------------------------------")
|
||||||
message("")
|
message("")
|
||||||
endmacro()
|
endmacro()
|
||||||
|
@ -1,41 +1,20 @@
|
|||||||
#################
|
.. _inline-assembly:
|
||||||
Solidity Assembly
|
|
||||||
#################
|
###############
|
||||||
|
Inline Assembly
|
||||||
|
###############
|
||||||
|
|
||||||
.. index:: ! assembly, ! asm, ! evmasm
|
.. index:: ! assembly, ! asm, ! evmasm
|
||||||
|
|
||||||
Solidity defines an assembly language that you can use without Solidity and also
|
|
||||||
as "inline assembly" inside Solidity source code. This guide starts with describing
|
|
||||||
how to use inline assembly, how it differs from standalone assembly
|
|
||||||
(sometimes also referred to by its proper name "Yul"), and
|
|
||||||
specifies assembly itself.
|
|
||||||
|
|
||||||
.. _inline-assembly:
|
|
||||||
|
|
||||||
Inline Assembly
|
|
||||||
===============
|
|
||||||
|
|
||||||
You can interleave Solidity statements with inline assembly in a language close
|
You can interleave Solidity statements with inline assembly in a language close
|
||||||
to the one of the virtual machine. This gives you more fine-grained control,
|
to the one of the Ethereum virtual machine. This gives you more fine-grained control,
|
||||||
especially when you are enhancing the language by writing libraries.
|
which is especially useful when you are enhancing the language by writing libraries.
|
||||||
|
|
||||||
As the EVM is a stack machine, it is often hard to address the correct stack slot
|
The language used for inline assembly in Solidity is called `Yul <yul>`_
|
||||||
and provide arguments to opcodes at the correct point on the stack. Solidity's inline
|
and it is documented in its own section. This section will only cover
|
||||||
assembly helps you do this, and with other issues that arise when writing manual assembly.
|
how the inline assembly code can interface with the surrounding Solidity code.
|
||||||
|
|
||||||
For inline assembly, the stack is actually not visible at all, but if you look
|
|
||||||
closer, there is always a very direct translation from inline assembly to
|
|
||||||
the stack based EVM opcode stream.
|
|
||||||
|
|
||||||
Inline assembly has the following features:
|
|
||||||
|
|
||||||
* functional-style opcodes: ``mul(1, add(2, 3))``
|
|
||||||
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
|
|
||||||
* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }``
|
|
||||||
* loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }``
|
|
||||||
* if statements: ``if slt(x, 0) { x := sub(0, x) }``
|
|
||||||
* switch statements: ``switch x case 0 { y := mul(x, 2) } default { y := 0 }``
|
|
||||||
* function calls: ``function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }``
|
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Inline assembly is a way to access the Ethereum Virtual Machine
|
Inline assembly is a way to access the Ethereum Virtual Machine
|
||||||
@ -43,24 +22,14 @@ Inline assembly has the following features:
|
|||||||
features and checks of Solidity. You should only use it for
|
features and checks of Solidity. You should only use it for
|
||||||
tasks that need it, and only if you are confident with using it.
|
tasks that need it, and only if you are confident with using it.
|
||||||
|
|
||||||
Syntax
|
|
||||||
------
|
|
||||||
|
|
||||||
Assembly parses comments, literals and identifiers in the same way as Solidity, so you can use the
|
An inline assembly block is marked by ``assembly { ... }``, where the code inside
|
||||||
usual ``//`` and ``/* */`` comments. There is one exception: Identifiers in inline assembly can contain
|
the curly braces is code in the `Yul <yul>`_ language.
|
||||||
``.``. Inline assembly is marked by ``assembly { ... }`` and inside
|
|
||||||
these curly braces, you can use the following (see the later sections for more details):
|
|
||||||
|
|
||||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
The inline assembly code can access local Solidity variables as explained below.
|
||||||
- opcodes in functional style, e.g. ``add(1, mload(0))``
|
|
||||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned)
|
|
||||||
- identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)``
|
|
||||||
- assignments, e.g. ``x := add(y, 3)``
|
|
||||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
|
||||||
|
|
||||||
Inline assembly manages local variables and control-flow. Because of that,
|
Different inline assembly blocks share no namespace, i.e. it is not possible
|
||||||
opcodes that interfere with these features are not available. This includes
|
to call a Yul function or access a Yul variable defined in a different inline assembly block.
|
||||||
the ``dup`` and ``swap`` instructions as well as ``jump`` instructions and labels.
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
@ -146,238 +115,20 @@ efficient code, for example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.. _opcodes:
|
|
||||||
|
|
||||||
Opcodes
|
|
||||||
-------
|
|
||||||
|
|
||||||
This document does not want to be a full description of the Ethereum virtual machine, but the
|
|
||||||
following list can be used as a quick reference of its opcodes.
|
|
||||||
|
|
||||||
If an opcode takes arguments, they are given in parentheses.
|
|
||||||
Opcodes marked with ``-`` do not return a result,
|
|
||||||
those marked with ``*`` are special in a certain way and all others return exactly one value.
|
|
||||||
Opcodes marked with ``F``, ``H``, ``B``, ``C`` or ``I`` are present since Frontier, Homestead,
|
|
||||||
Byzantium, Constantinople or Istanbul, respectively.
|
|
||||||
|
|
||||||
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
|
|
||||||
but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``.
|
|
||||||
|
|
||||||
In the grammar, opcodes are represented as pre-defined identifiers ("built-in functions").
|
|
||||||
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| Instruction | | | Explanation |
|
|
||||||
+=========================+=====+===+=================================================================+
|
|
||||||
| stop() + `-` | F | stop execution, identical to return(0, 0) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| add(x, y) | | F | x + y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sub(x, y) | | F | x - y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mul(x, y) | | F | x * y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| div(x, y) | | F | x / y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sdiv(x, y) | | F | x / y, for signed numbers in two's complement |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mod(x, y) | | F | x % y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| smod(x, y) | | F | x % y, for signed numbers in two's complement |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| exp(x, y) | | F | x to the power of y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| not(x) | | F | ~x, every bit of x is negated |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| lt(x, y) | | F | 1 if x < y, 0 otherwise |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| gt(x, y) | | F | 1 if x > y, 0 otherwise |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| eq(x, y) | | F | 1 if x == y, 0 otherwise |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| iszero(x) | | F | 1 if x == 0, 0 otherwise |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| and(x, y) | | F | bitwise "and" of x and y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| or(x, y) | | F | bitwise "or" of x and y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| xor(x, y) | | F | bitwise "xor" of x and y |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| shl(x, y) | | C | logical shift left y by x bits |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| shr(x, y) | | C | logical shift right y by x bits |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sar(x, y) | | C | signed arithmetic shift right y by x bits |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| pc() | | F | current position in code |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| pop(x) | `-` | F | discard value x |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mload(p) | | F | mem[p...(p+32)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mstore(p, v) | `-` | F | mem[p...(p+32)) := v |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sload(p) | | F | storage[p] |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| sstore(p, v) | `-` | F | storage[p] := v |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| msize() | | F | size of memory, i.e. largest accessed memory index |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| gas() | | F | gas still available to execution |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| address() | | F | address of the current contract / execution context |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| balance(a) | | F | wei balance at address a |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| selfbalance() | | I | equivalent to balance(address()), but cheaper |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| caller() | | F | call sender (excluding ``delegatecall``) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| callvalue() | | F | wei sent together with the current call |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| calldataload(p) | | F | call data starting from position p (32 bytes) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| calldatasize() | | F | size of call data in bytes |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| codesize() | | F | size of the code of the current contract / execution context |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| extcodesize(a) | | F | size of the code at address a |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| returndatasize() | | B | size of the last returndata |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| extcodehash(a) | | C | code hash of address a |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei |
|
|
||||||
| | | | and return the new address |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
|
|
||||||
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
|
||||||
| | | | and send v wei and return the new address, where ``0xff`` is a |
|
|
||||||
| | | | 1 byte value, ``this`` is the current contract's address |
|
|
||||||
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
|
|
||||||
| insize, out, outsize) | | | providing g gas and v wei and output area |
|
|
||||||
| | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) |
|
|
||||||
| | | | and 1 on success |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
|
|
||||||
| insize, out, outsize) | | | in the context of the current contract otherwise |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` |
|
|
||||||
| insize, out, outsize) | | | and ``callvalue`` |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
|
|
||||||
| insize, out, outsize) | | | not allow state modifications |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| invalid() | `-` | F | end execution with invalid instruction |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) |
|
|
||||||
| t4) | | | |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| chainid() | | I | ID of the executing chain (EIP 1344) |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| origin() | | F | transaction sender |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| gasprice() | | F | gas price of the transaction |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| coinbase() | | F | current mining beneficiary |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| timestamp() | | F | timestamp of the current block in seconds since the epoch |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| number() | | F | current block number |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| difficulty() | | F | difficulty of the current block |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
| gaslimit() | | F | block gas limit of the current block |
|
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
|
||||||
|
|
||||||
Literals
|
|
||||||
--------
|
|
||||||
|
|
||||||
You can use integer constants by typing them in decimal or hexadecimal notation and an
|
|
||||||
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
|
|
||||||
to add 2 and 3 resulting in 5 and then computes the bitwise ``AND`` with the string "abc".
|
|
||||||
The final value is assigned to a local variable called ``x``.
|
|
||||||
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
assembly { let x := and("abc", add(3, 2)) }
|
|
||||||
|
|
||||||
|
|
||||||
Functional Style
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
For a sequence of opcodes, it is often hard to see what the actual
|
|
||||||
arguments for certain opcodes are. In the following example,
|
|
||||||
``3`` is added to the contents in memory at position ``0x80``.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
3 0x80 mload add 0x80 mstore
|
|
||||||
|
|
||||||
Solidity inline assembly has a "functional style" notation where the same code
|
|
||||||
would be written as follows:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
mstore(0x80, add(mload(0x80), 3))
|
|
||||||
|
|
||||||
If you read the code from right to left, you end up with exactly the same
|
|
||||||
sequence of constants and opcodes, but it is much clearer where the
|
|
||||||
values end up.
|
|
||||||
|
|
||||||
If you care about the exact stack layout, just note that the
|
|
||||||
syntactically first argument for a function or opcode will be put at the
|
|
||||||
top of the stack.
|
|
||||||
|
|
||||||
Access to External Variables, Functions and Libraries
|
Access to External Variables, Functions and Libraries
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
You can access Solidity variables and other identifiers by using their name.
|
You can access Solidity variables and other identifiers by using their name.
|
||||||
For variables stored in the memory data location, this pushes the address, and not the value
|
|
||||||
onto the stack. Variables stored in the storage data location are different, as they might not
|
Local variables of value type are directly usable in inline assembly.
|
||||||
occupy a full storage slot, so their "address" is composed of a slot and a byte-offset
|
|
||||||
|
Local variables that refer to memory or calldata evaluate to the
|
||||||
|
address of the variable in memory, resp. calldata, not the value itself.
|
||||||
|
|
||||||
|
For local storage variables or state variables, a single Yul identifier
|
||||||
|
is not sufficient, since they do not necessarily occupy a single full storage slot.
|
||||||
|
Therefore, their "address" is composed of a slot and a byte-offset
|
||||||
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
|
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
|
||||||
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
|
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
|
||||||
|
|
||||||
@ -391,7 +142,9 @@ Local Solidity variables are available for assignments, for example:
|
|||||||
uint b;
|
uint b;
|
||||||
function f(uint x) public view returns (uint r) {
|
function f(uint x) public view returns (uint r) {
|
||||||
assembly {
|
assembly {
|
||||||
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
|
// We ignore the storage slot offset, we know it is zero
|
||||||
|
// in this special case.
|
||||||
|
r := mul(x, sload(b_slot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,177 +160,19 @@ Local Solidity variables are available for assignments, for example:
|
|||||||
To clean signed types, you can use the ``signextend`` opcode:
|
To clean signed types, you can use the ``signextend`` opcode:
|
||||||
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
|
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
|
||||||
|
|
||||||
Declaring Assembly-Local Variables
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
You can use the ``let`` keyword to declare variables that are only visible in
|
Since Solidity 0.6.0 the name of a inline assembly variable may not end in ``_offset`` or ``_slot``
|
||||||
inline assembly and actually only in the current ``{...}``-block. What happens
|
|
||||||
is that the ``let`` instruction will create a new stack slot that is reserved
|
|
||||||
for the variable and automatically removed again when the end of the block
|
|
||||||
is reached. You need to provide an initial value for the variable which can
|
|
||||||
be just ``0``, but it can also be a complex functional-style expression.
|
|
||||||
|
|
||||||
Since 0.6.0 the name of a declared variable may not end in ``_offset`` or ``_slot``
|
|
||||||
and it may not shadow any declaration visible in the scope of the inline assembly block
|
and it may not shadow any declaration visible in the scope of the inline assembly block
|
||||||
(including variable, contract and function declarations). Similarly, if the name of a declared
|
(including variable, contract and function declarations). Similarly, if the name of a declared
|
||||||
variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any
|
variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any
|
||||||
declaration visible in the scope of the inline assembly block.
|
declaration visible in the scope of the inline assembly block.
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
|
||||||
|
|
||||||
contract C {
|
|
||||||
function f(uint x) public view returns (uint b) {
|
|
||||||
assembly {
|
|
||||||
let v := add(x, 1)
|
|
||||||
mstore(0x80, v)
|
|
||||||
{
|
|
||||||
let y := add(sload(v), 1)
|
|
||||||
b := y
|
|
||||||
} // y is "deallocated" here
|
|
||||||
b := add(b, v)
|
|
||||||
} // v is "deallocated" here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Assignments
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Assignments are possible to assembly-local variables and to function-local
|
Assignments are possible to assembly-local variables and to function-local
|
||||||
variables. Take care that when you assign to variables that point to
|
variables. Take care that when you assign to variables that point to
|
||||||
memory or storage, you will only change the pointer and not the data.
|
memory or storage, you will only change the pointer and not the data.
|
||||||
|
|
||||||
Variables can only be assigned expressions that result in exactly one value.
|
|
||||||
If you want to assign the values returned from a function that has
|
|
||||||
multiple return parameters, you have to provide multiple variables.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
let v := 0
|
|
||||||
let g := add(v, 2)
|
|
||||||
function f() -> a, b { }
|
|
||||||
let c, d := f()
|
|
||||||
}
|
|
||||||
|
|
||||||
If
|
|
||||||
--
|
|
||||||
|
|
||||||
The if statement can be used for conditionally executing code.
|
|
||||||
There is no "else" part, consider using "switch" (see below) if
|
|
||||||
you need multiple alternatives.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
if eq(value, 0) { revert(0, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
The curly braces for the body are required.
|
|
||||||
|
|
||||||
Switch
|
|
||||||
------
|
|
||||||
|
|
||||||
You can use a switch statement as a very basic version of "if/else".
|
|
||||||
It takes the value of an expression and compares it to several constants.
|
|
||||||
The branch corresponding to the matching constant is taken. Contrary to the
|
|
||||||
error-prone behaviour of some programming languages, control flow does
|
|
||||||
not continue from one case to the next. There can be a fallback or default
|
|
||||||
case called ``default``.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
let x := 0
|
|
||||||
switch calldataload(4)
|
|
||||||
case 0 {
|
|
||||||
x := calldataload(0x24)
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
x := calldataload(0x44)
|
|
||||||
}
|
|
||||||
sstore(0, div(x, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
The list of cases does not require curly braces, but the body of a
|
|
||||||
case does require them.
|
|
||||||
|
|
||||||
Loops
|
|
||||||
-----
|
|
||||||
|
|
||||||
Assembly supports a simple for-style loop. For-style loops have
|
|
||||||
a header containing an initializing part, a condition and a post-iteration
|
|
||||||
part. The condition has to be a functional-style expression, while
|
|
||||||
the other two are blocks. If the initializing part
|
|
||||||
declares any variables, the scope of these variables is extended into the
|
|
||||||
body (including the condition and the post-iteration part).
|
|
||||||
|
|
||||||
The ``break`` and ``continue`` statements can be used to exit the loop
|
|
||||||
or skip to the post-part, respectively.
|
|
||||||
|
|
||||||
The following example computes the sum of an area in memory.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
let x := 0
|
|
||||||
for { let i := 0 } lt(i, 0x100) { i := add(i, 0x20) } {
|
|
||||||
x := add(x, mload(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
For loops can also be written so that they behave like while loops:
|
|
||||||
Simply leave the initialization and post-iteration parts empty.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
let x := 0
|
|
||||||
let i := 0
|
|
||||||
for { } lt(i, 0x100) { } { // while(i < 0x100)
|
|
||||||
x := add(x, mload(i))
|
|
||||||
i := add(i, 0x20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Functions
|
|
||||||
---------
|
|
||||||
|
|
||||||
Assembly allows the definition of low-level functions. These take their
|
|
||||||
arguments (and a return PC) from the stack and also put the results onto the
|
|
||||||
stack. Calling a function looks the same way as executing a functional-style
|
|
||||||
opcode.
|
|
||||||
|
|
||||||
Functions can be defined anywhere and are visible in the block they are
|
|
||||||
declared in. Inside a function, you cannot access local variables
|
|
||||||
defined outside of that function.
|
|
||||||
|
|
||||||
If you call a function that returns multiple values, you have to assign
|
|
||||||
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
|
|
||||||
|
|
||||||
The ``leave`` statement can be used to exit the current function. It
|
|
||||||
works like the ``return`` statement in other languages just that it does
|
|
||||||
not take a value to return, it just exits the functions and the function
|
|
||||||
will return whatever values are currently assigned to the return variable(s).
|
|
||||||
|
|
||||||
The following example implements the power function by square-and-multiply.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
function power(base, exponent) -> result {
|
|
||||||
switch exponent
|
|
||||||
case 0 { result := 1 }
|
|
||||||
case 1 { result := base }
|
|
||||||
default {
|
|
||||||
result := power(mul(base, base), div(exponent, 2))
|
|
||||||
switch mod(exponent, 2)
|
|
||||||
case 1 { result := mul(base, result) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Things to Avoid
|
Things to Avoid
|
||||||
---------------
|
---------------
|
||||||
@ -593,7 +188,8 @@ Conventions in Solidity
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
|
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
|
||||||
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that types can be shorter than 256
|
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
|
||||||
|
types can be shorter than 256
|
||||||
bits, and the higher-order bits are cleaned when necessary,
|
bits, and the higher-order bits are cleaned when necessary,
|
||||||
i.e., shortly before they are written to memory or before comparisons are performed.
|
i.e., shortly before they are written to memory or before comparisons are performed.
|
||||||
This means that if you access such a variable
|
This means that if you access such a variable
|
||||||
@ -630,157 +226,3 @@ first slot of the array and followed by the array elements.
|
|||||||
to allow better convertibility between statically- and dynamically-sized arrays, so
|
to allow better convertibility between statically- and dynamically-sized arrays, so
|
||||||
do not rely on this.
|
do not rely on this.
|
||||||
|
|
||||||
|
|
||||||
Standalone Assembly
|
|
||||||
===================
|
|
||||||
|
|
||||||
The assembly language described as inline assembly above can also be used
|
|
||||||
standalone and in fact, the plan is to use it as an intermediate language
|
|
||||||
for the Solidity compiler. In this form, it tries to achieve several goals:
|
|
||||||
|
|
||||||
1. Programs written in it should be readable, even if the code is generated by a compiler from Solidity.
|
|
||||||
2. The translation from assembly to bytecode should contain as few "surprises" as possible.
|
|
||||||
3. Control flow should be easy to detect to help in formal verification and optimization.
|
|
||||||
|
|
||||||
In order to achieve the first and last goal, assembly provides high-level constructs
|
|
||||||
like ``for`` loops, ``if`` and ``switch`` statements and function calls. It should be possible
|
|
||||||
to write assembly programs that do not make use of explicit ``SWAP``, ``DUP``,
|
|
||||||
``JUMP`` and ``JUMPI`` statements, because the first two obfuscate the data flow
|
|
||||||
and the last two obfuscate control flow. Furthermore, functional statements of
|
|
||||||
the form ``mul(add(x, y), 7)`` are preferred over pure opcode statements like
|
|
||||||
``7 y x add mul`` because in the first form, it is much easier to see which
|
|
||||||
operand is used for which opcode.
|
|
||||||
|
|
||||||
The second goal is achieved by compiling the
|
|
||||||
higher level constructs to bytecode in a very regular way.
|
|
||||||
The only non-local operation performed
|
|
||||||
by the assembler is name lookup of user-defined identifiers (functions, variables, ...),
|
|
||||||
which follow very simple and regular scoping rules and cleanup of local variables from the stack.
|
|
||||||
|
|
||||||
Scoping: An identifier that is declared (label, variable, function, assembly)
|
|
||||||
is only visible in the block where it was declared (including nested blocks
|
|
||||||
inside the current block). It is not legal to access local variables across
|
|
||||||
function borders, even if they would be in scope. Shadowing is not allowed.
|
|
||||||
Local variables cannot be accessed before they were declared, but
|
|
||||||
functions and assemblies can. Assemblies are special blocks that are used
|
|
||||||
for e.g. returning runtime code or creating contracts. No identifier from an
|
|
||||||
outer assembly is visible in a sub-assembly.
|
|
||||||
|
|
||||||
If control flow passes over the end of a block, pop instructions are inserted
|
|
||||||
that match the number of local variables declared in that block.
|
|
||||||
Whenever a local variable is referenced, the code generator needs
|
|
||||||
to know its current relative position in the stack and thus it needs to
|
|
||||||
keep track of the current so-called stack height. Since all local variables
|
|
||||||
are removed at the end of a block, the stack height before and after the block
|
|
||||||
should be the same. If this is not the case, compilation fails.
|
|
||||||
|
|
||||||
Using ``switch``, ``for`` and functions, it should be possible to write
|
|
||||||
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
|
|
||||||
easier to analyze the control flow, which allows for improved formal
|
|
||||||
verification and optimization.
|
|
||||||
|
|
||||||
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
|
|
||||||
The position of all local variables on the stack needs to be known, otherwise
|
|
||||||
neither references to local variables nor removing local variables automatically
|
|
||||||
from the stack at the end of a block will work properly.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
We will follow an example compilation from Solidity to assembly.
|
|
||||||
We consider the runtime bytecode of the following Solidity program::
|
|
||||||
|
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
|
||||||
|
|
||||||
|
|
||||||
contract C {
|
|
||||||
function f(uint x) public pure returns (uint y) {
|
|
||||||
y = 1;
|
|
||||||
for (uint i = 0; i < x; i++)
|
|
||||||
y = 2 * y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The following assembly will be generated::
|
|
||||||
|
|
||||||
{
|
|
||||||
mstore(0x40, 0x80) // store the "free memory pointer"
|
|
||||||
// function dispatcher
|
|
||||||
switch div(calldataload(0), exp(2, 226))
|
|
||||||
case 0xb3de648b {
|
|
||||||
let r := f(calldataload(4))
|
|
||||||
let ret := $allocate(0x20)
|
|
||||||
mstore(ret, r)
|
|
||||||
return(ret, 0x20)
|
|
||||||
}
|
|
||||||
default { revert(0, 0) }
|
|
||||||
// memory allocator
|
|
||||||
function $allocate(size) -> pos {
|
|
||||||
pos := mload(0x40)
|
|
||||||
mstore(0x40, add(pos, size))
|
|
||||||
}
|
|
||||||
// the contract function
|
|
||||||
function f(x) -> y {
|
|
||||||
y := 1
|
|
||||||
for { let i := 0 } lt(i, x) { i := add(i, 1) } {
|
|
||||||
y := mul(2, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Assembly Grammar
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The tasks of the parser are the following:
|
|
||||||
|
|
||||||
- Turn the byte stream into a token stream, discarding C++-style comments
|
|
||||||
(a special comment exists for source references, but we will not explain it here).
|
|
||||||
- Turn the token stream into an AST according to the grammar below
|
|
||||||
- Register identifiers with the block they are defined in (annotation to the
|
|
||||||
AST node) and note from which point on, variables can be accessed.
|
|
||||||
|
|
||||||
The assembly lexer follows the one defined by Solidity itself.
|
|
||||||
|
|
||||||
Whitespace is used to delimit tokens and it consists of the characters
|
|
||||||
Space, Tab and Linefeed. Comments are regular JavaScript/C++ comments and
|
|
||||||
are interpreted in the same way as Whitespace.
|
|
||||||
|
|
||||||
Grammar::
|
|
||||||
|
|
||||||
AssemblyBlock = '{' AssemblyItem* '}'
|
|
||||||
AssemblyItem =
|
|
||||||
Identifier |
|
|
||||||
AssemblyBlock |
|
|
||||||
AssemblyExpression |
|
|
||||||
AssemblyLocalDefinition |
|
|
||||||
AssemblyAssignment |
|
|
||||||
AssemblyIf |
|
|
||||||
AssemblySwitch |
|
|
||||||
AssemblyFunctionDefinition |
|
|
||||||
AssemblyFor |
|
|
||||||
'break' |
|
|
||||||
'continue' |
|
|
||||||
'leave' |
|
|
||||||
SubAssembly
|
|
||||||
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
|
|
||||||
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
|
|
||||||
Identifier = [a-zA-Z_$] [a-zA-Z_0-9.]*
|
|
||||||
AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
|
|
||||||
AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )?
|
|
||||||
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
|
|
||||||
IdentifierOrList = Identifier | '(' IdentifierList ')'
|
|
||||||
IdentifierList = Identifier ( ',' Identifier)*
|
|
||||||
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
|
|
||||||
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
|
|
||||||
( 'default' AssemblyBlock )?
|
|
||||||
AssemblyCase = 'case' AssemblyExpression AssemblyBlock
|
|
||||||
AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')'
|
|
||||||
( '->' '(' IdentifierList ')' )? AssemblyBlock
|
|
||||||
AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression )
|
|
||||||
AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock
|
|
||||||
SubAssembly = 'assembly' Identifier AssemblyBlock
|
|
||||||
NumberLiteral = HexNumber | DecimalNumber
|
|
||||||
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
|
|
||||||
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
|
|
||||||
HexNumber = '0x' [0-9a-fA-F]+
|
|
||||||
DecimalNumber = [0-9]+
|
|
||||||
|
@ -880,5 +880,9 @@
|
|||||||
"0.6.1": {
|
"0.6.1": {
|
||||||
"bugs": [],
|
"bugs": [],
|
||||||
"released": "2020-01-02"
|
"released": "2020-01-02"
|
||||||
|
},
|
||||||
|
"0.6.2": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2020-01-27"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -39,12 +39,12 @@ if they are marked ``virtual``. For details, please see
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract mortal is owned {
|
contract destructible is owned {
|
||||||
// This contract inherits the `onlyOwner` modifier from
|
// This contract inherits the `onlyOwner` modifier from
|
||||||
// `owned` and applies it to the `close` function, which
|
// `owned` and applies it to the `destroy` function, which
|
||||||
// causes that calls to `close` only have an effect if
|
// causes that calls to `destroy` only have an effect if
|
||||||
// they are made by the stored owner.
|
// they are made by the stored owner.
|
||||||
function close() public onlyOwner {
|
function destroy() public onlyOwner {
|
||||||
selfdestruct(owner);
|
selfdestruct(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ if they are marked ``virtual``. For details, please see
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Register is priced, owned {
|
contract Register is priced, destructible {
|
||||||
mapping (address => bool) registeredAddresses;
|
mapping (address => bool) registeredAddresses;
|
||||||
uint price;
|
uint price;
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity >0.6.1 <0.7.0;
|
||||||
|
|
||||||
contract Test {
|
contract Test {
|
||||||
// This function is called for all messages sent to
|
// This function is called for all messages sent to
|
||||||
@ -382,7 +382,7 @@ operations as long as there is enough gas passed on to it.
|
|||||||
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
|
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
|
||||||
require(success);
|
require(success);
|
||||||
// results in test.x becoming == 1 and test.y becoming 0.
|
// results in test.x becoming == 1 and test.y becoming 0.
|
||||||
(success,) = address(test).call.value(1)(abi.encodeWithSignature("nonExistingFunction()"));
|
(success,) = address(test).call{value: 1}(abi.encodeWithSignature("nonExistingFunction()"));
|
||||||
require(success);
|
require(success);
|
||||||
// results in test.x becoming == 1 and test.y becoming 1.
|
// results in test.x becoming == 1 and test.y becoming 1.
|
||||||
|
|
||||||
|
@ -51,10 +51,10 @@ Details are given in the following example.
|
|||||||
// contracts can access all non-private members including
|
// contracts can access all non-private members including
|
||||||
// internal functions and state variables. These cannot be
|
// internal functions and state variables. These cannot be
|
||||||
// accessed externally via `this`, though.
|
// accessed externally via `this`, though.
|
||||||
contract Mortal is Owned {
|
contract Destructible is Owned {
|
||||||
// The keyword `virtual` means that the function can change
|
// The keyword `virtual` means that the function can change
|
||||||
// its behaviour in derived classes ("overriding").
|
// its behaviour in derived classes ("overriding").
|
||||||
function kill() virtual public {
|
function destroy() virtual public {
|
||||||
if (msg.sender == owner) selfdestruct(owner);
|
if (msg.sender == owner) selfdestruct(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,9 +76,9 @@ Details are given in the following example.
|
|||||||
|
|
||||||
|
|
||||||
// Multiple inheritance is possible. Note that `owned` is
|
// Multiple inheritance is possible. Note that `owned` is
|
||||||
// also a base class of `mortal`, yet there is only a single
|
// also a base class of `Destructible`, yet there is only a single
|
||||||
// instance of `owned` (as for virtual inheritance in C++).
|
// instance of `owned` (as for virtual inheritance in C++).
|
||||||
contract Named is Owned, Mortal {
|
contract Named is Owned, Destructible {
|
||||||
constructor(bytes32 name) public {
|
constructor(bytes32 name) public {
|
||||||
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
||||||
NameReg(config.lookup(1)).register(name);
|
NameReg(config.lookup(1)).register(name);
|
||||||
@ -92,13 +92,13 @@ Details are given in the following example.
|
|||||||
// If you want the function to override, you need to use the
|
// If you want the function to override, you need to use the
|
||||||
// `override` keyword. You need to specify the `virtual` keyword again
|
// `override` keyword. You need to specify the `virtual` keyword again
|
||||||
// if you want this function to be overridden again.
|
// if you want this function to be overridden again.
|
||||||
function kill() public virtual override {
|
function destroy() public virtual override {
|
||||||
if (msg.sender == owner) {
|
if (msg.sender == owner) {
|
||||||
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
||||||
NameReg(config.lookup(1)).unregister();
|
NameReg(config.lookup(1)).unregister();
|
||||||
// It is still possible to call a specific
|
// It is still possible to call a specific
|
||||||
// overridden function.
|
// overridden function.
|
||||||
Mortal.kill();
|
Destructible.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,21 +107,21 @@ Details are given in the following example.
|
|||||||
// If a constructor takes an argument, it needs to be
|
// If a constructor takes an argument, it needs to be
|
||||||
// provided in the header (or modifier-invocation-style at
|
// provided in the header (or modifier-invocation-style at
|
||||||
// the constructor of the derived contract (see below)).
|
// the constructor of the derived contract (see below)).
|
||||||
contract PriceFeed is Owned, Mortal, Named("GoldFeed") {
|
contract PriceFeed is Owned, Destructible, Named("GoldFeed") {
|
||||||
function updateInfo(uint newInfo) public {
|
function updateInfo(uint newInfo) public {
|
||||||
if (msg.sender == owner) info = newInfo;
|
if (msg.sender == owner) info = newInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here, we only specify `override` and not `virtual`.
|
// Here, we only specify `override` and not `virtual`.
|
||||||
// This means that contracts deriving from `PriceFeed`
|
// This means that contracts deriving from `PriceFeed`
|
||||||
// cannot change the behaviour of `kill` anymore.
|
// cannot change the behaviour of `destroy` anymore.
|
||||||
function kill() public override(Mortal, Named) { Named.kill(); }
|
function destroy() public override(Destructible, Named) { Named.destroy(); }
|
||||||
function get() public view returns(uint r) { return info; }
|
function get() public view returns(uint r) { return info; }
|
||||||
|
|
||||||
uint info;
|
uint info;
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that above, we call ``mortal.kill()`` to "forward" the
|
Note that above, we call ``Destructible.destroy()`` to "forward" the
|
||||||
destruction request. The way this is done is problematic, as
|
destruction request. The way this is done is problematic, as
|
||||||
seen in the following example::
|
seen in the following example::
|
||||||
|
|
||||||
@ -132,27 +132,27 @@ seen in the following example::
|
|||||||
address payable owner;
|
address payable owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
contract mortal is owned {
|
contract Destructible is owned {
|
||||||
function kill() public virtual {
|
function destroy() public virtual {
|
||||||
if (msg.sender == owner) selfdestruct(owner);
|
if (msg.sender == owner) selfdestruct(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Base1 is mortal {
|
contract Base1 is Destructible {
|
||||||
function kill() public virtual override { /* do cleanup 1 */ mortal.kill(); }
|
function destroy() public virtual override { /* do cleanup 1 */ Destructible.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Base2 is mortal {
|
contract Base2 is Destructible {
|
||||||
function kill() public virtual override { /* do cleanup 2 */ mortal.kill(); }
|
function destroy() public virtual override { /* do cleanup 2 */ Destructible.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Final is Base1, Base2 {
|
contract Final is Base1, Base2 {
|
||||||
function kill() public override(Base1, Base2) { Base2.kill(); }
|
function destroy() public override(Base1, Base2) { Base2.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
A call to ``Final.kill()`` will call ``Base2.kill`` because we specify it
|
A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
|
||||||
explicitly in the final override, but this function will bypass
|
explicitly in the final override, but this function will bypass
|
||||||
``Base1.kill``. The way around this is to use ``super``::
|
``Base1.destroy``. The way around this is to use ``super``::
|
||||||
|
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
@ -161,31 +161,31 @@ explicitly in the final override, but this function will bypass
|
|||||||
address payable owner;
|
address payable owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
contract mortal is owned {
|
contract Destructible is owned {
|
||||||
function kill() virtual public {
|
function destroy() virtual public {
|
||||||
if (msg.sender == owner) selfdestruct(owner);
|
if (msg.sender == owner) selfdestruct(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Base1 is mortal {
|
contract Base1 is Destructible {
|
||||||
function kill() public virtual override { /* do cleanup 1 */ super.kill(); }
|
function destroy() public virtual override { /* do cleanup 1 */ super.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
contract Base2 is mortal {
|
contract Base2 is Destructible {
|
||||||
function kill() public virtual override { /* do cleanup 2 */ super.kill(); }
|
function destroy() public virtual override { /* do cleanup 2 */ super.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
contract Final is Base1, Base2 {
|
contract Final is Base1, Base2 {
|
||||||
function kill() public override(Base1, Base2) { super.kill(); }
|
function destroy() public override(Base1, Base2) { super.destroy(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
If ``Base2`` calls a function of ``super``, it does not simply
|
If ``Base2`` calls a function of ``super``, it does not simply
|
||||||
call this function on one of its base contracts. Rather, it
|
call this function on one of its base contracts. Rather, it
|
||||||
calls this function on the next base contract in the final
|
calls this function on the next base contract in the final
|
||||||
inheritance graph, so it will call ``Base1.kill()`` (note that
|
inheritance graph, so it will call ``Base1.destroy()`` (note that
|
||||||
the final inheritance sequence is -- starting with the most
|
the final inheritance sequence is -- starting with the most
|
||||||
derived contract: Final, Base2, Base1, mortal, owned).
|
derived contract: Final, Base2, Base1, Destructible, owned).
|
||||||
The actual function that is called when using super is
|
The actual function that is called when using super is
|
||||||
not known in the context of the class where it is used,
|
not known in the context of the class where it is used,
|
||||||
although its type is known. This is similar for ordinary
|
although its type is known. This is similar for ordinary
|
||||||
|
@ -8,7 +8,7 @@ Interfaces
|
|||||||
|
|
||||||
Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions:
|
Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions:
|
||||||
|
|
||||||
- They cannot inherit other contracts or interfaces.
|
- They cannot inherit from other contracts, but they can inherit from other interfaces.
|
||||||
- All declared functions must be external.
|
- All declared functions must be external.
|
||||||
- They cannot declare a constructor.
|
- They cannot declare a constructor.
|
||||||
- They cannot declare state variables.
|
- They cannot declare state variables.
|
||||||
@ -37,6 +37,27 @@ they can be overridden. This does not automatically mean that an overriding func
|
|||||||
can be overridden again - this is only possible if the overriding
|
can be overridden again - this is only possible if the overriding
|
||||||
function is marked ``virtual``.
|
function is marked ``virtual``.
|
||||||
|
|
||||||
|
Interfaces can inherit from other interfaces. This has the same rules as normal
|
||||||
|
inheritance.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity >0.6.1 <0.7.0;
|
||||||
|
|
||||||
|
interface ParentA {
|
||||||
|
function test() external returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParentB {
|
||||||
|
function test() external returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubInterface is ParentA, ParentB {
|
||||||
|
// Must redefine test in order to assert that the parent
|
||||||
|
// meanings are compatible.
|
||||||
|
function test() external override(ParentA, ParentB) returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
Types defined inside interfaces and other contract-like structures
|
Types defined inside interfaces and other contract-like structures
|
||||||
can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``.
|
can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``.
|
||||||
|
|
||||||
|
@ -328,7 +328,7 @@ Whiskers
|
|||||||
compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
|
compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
|
||||||
|
|
||||||
The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are
|
The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are
|
||||||
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly`
|
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`yul`
|
||||||
(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
|
(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
|
||||||
Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future.
|
Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future.
|
||||||
|
|
||||||
|
@ -75,9 +75,10 @@ all function arguments have to be copied to memory.
|
|||||||
it is a message call as part of the overall transaction.
|
it is a message call as part of the overall transaction.
|
||||||
|
|
||||||
When calling functions of other contracts, you can specify the amount of Wei or
|
When calling functions of other contracts, you can specify the amount of Wei or
|
||||||
gas sent with the call with the special options ``.value()`` and ``.gas()``,
|
gas sent with the call with the special options ``{value: 10, gas: 10000}``.
|
||||||
respectively. Any Wei you send to the contract is added to the total balance
|
Note that it is discouraged to specify gas values explicitly, since the gas costs
|
||||||
of the contract:
|
of opcodes can change in the future. Any Wei you send to the contract is added
|
||||||
|
to the total balance of that contract:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -90,14 +91,14 @@ of the contract:
|
|||||||
contract Consumer {
|
contract Consumer {
|
||||||
InfoFeed feed;
|
InfoFeed feed;
|
||||||
function setFeed(InfoFeed addr) public { feed = addr; }
|
function setFeed(InfoFeed addr) public { feed = addr; }
|
||||||
function callFeed() public { feed.info.value(10).gas(800)(); }
|
function callFeed() public { feed.info{value: 10, gas: 800}(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
You need to use the modifier ``payable`` with the ``info`` function because
|
You need to use the modifier ``payable`` with the ``info`` function because
|
||||||
otherwise, the ``.value()`` option would not be available.
|
otherwise, the ``value`` option would not be available.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Be careful that ``feed.info.value(10).gas(800)`` only locally sets the
|
Be careful that ``feed.info{value: 10, gas: 800}`` only locally sets the
|
||||||
``value`` and amount of ``gas`` sent with the function call, and the
|
``value`` and amount of ``gas`` sent with the function call, and the
|
||||||
parentheses at the end perform the actual call. So in this case, the
|
parentheses at the end perform the actual call. So in this case, the
|
||||||
function is not called and the ``value`` and ``gas`` settings are lost.
|
function is not called and the ``value`` and ``gas`` settings are lost.
|
||||||
@ -121,6 +122,11 @@ throws an exception or goes out of gas.
|
|||||||
external functions happen after any changes to state variables in your contract
|
external functions happen after any changes to state variables in your contract
|
||||||
so your contract is not vulnerable to a reentrancy exploit.
|
so your contract is not vulnerable to a reentrancy exploit.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Before Solidity 0.6.2, the recommended way to specify the value and gas
|
||||||
|
was to use ``f.value(x).gas(g)()``. This is still possible but deprecated
|
||||||
|
and will be removed with Solidity 0.7.0.
|
||||||
|
|
||||||
Named Calls and Anonymous Function Parameters
|
Named Calls and Anonymous Function Parameters
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
@ -196,17 +202,81 @@ is compiled so recursive creation-dependencies are not possible.
|
|||||||
|
|
||||||
function createAndEndowD(uint arg, uint amount) public payable {
|
function createAndEndowD(uint arg, uint amount) public payable {
|
||||||
// Send ether along with the creation
|
// Send ether along with the creation
|
||||||
D newD = (new D).value(amount)(arg);
|
D newD = new D{value: amount}(arg);
|
||||||
newD.x();
|
newD.x();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
As seen in the example, it is possible to send Ether while creating
|
As seen in the example, it is possible to send Ether while creating
|
||||||
an instance of ``D`` using the ``.value()`` option, but it is not possible
|
an instance of ``D`` using the ``value`` option, but it is not possible
|
||||||
to limit the amount of gas.
|
to limit the amount of gas.
|
||||||
If the creation fails (due to out-of-stack, not enough balance or other problems),
|
If the creation fails (due to out-of-stack, not enough balance or other problems),
|
||||||
an exception is thrown.
|
an exception is thrown.
|
||||||
|
|
||||||
|
Salted contract creations / create2
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
When creating a contract, the address of the contract is computed from
|
||||||
|
the address of the creating contract and a counter that is increased with
|
||||||
|
each contract creation.
|
||||||
|
|
||||||
|
If you specify the option ``salt`` (a bytes32 value), then contract creation will
|
||||||
|
use a different mechanism to come up with the address of the new contract:
|
||||||
|
|
||||||
|
It will compute the address from the address of the creating contract,
|
||||||
|
the given salt value, the (creation) bytecode of the created contract and the constructor
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
In particular, the counter ("nonce") is not used. This allows for more flexibility
|
||||||
|
in creating contracts: You are able to derive the address of the
|
||||||
|
new contract before it is created. Furthermore, you can rely on this address
|
||||||
|
also in case the creating
|
||||||
|
contracts creates other contracts in the meantime.
|
||||||
|
|
||||||
|
The main use-case here is contracts that act as judges for off-chain interactions,
|
||||||
|
which only need to be created if there is a dispute.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pragma solidity >0.6.1 <0.7.0;
|
||||||
|
|
||||||
|
contract D {
|
||||||
|
uint public x;
|
||||||
|
constructor(uint a) public {
|
||||||
|
x = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function createDSalted(bytes32 salt, uint arg) public {
|
||||||
|
/// This complicated expression just tells you how the address
|
||||||
|
/// can be pre-computed. It is just there for illustration.
|
||||||
|
/// You actually only need ``new D{salt: salt}(arg)``.
|
||||||
|
address predictedAddress = address(bytes20(keccak256(abi.encodePacked(
|
||||||
|
byte(0xff),
|
||||||
|
address(this),
|
||||||
|
salt,
|
||||||
|
keccak256(abi.encodePacked(
|
||||||
|
type(D).creationCode,
|
||||||
|
arg
|
||||||
|
))
|
||||||
|
))));
|
||||||
|
|
||||||
|
D d = new D{salt: salt}(arg);
|
||||||
|
require(address(d) == predictedAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
There are some peculiarities in relation to salted creation. A contract can be
|
||||||
|
re-created at the same address after having been destroyed. Yet, it is possible
|
||||||
|
for that newly created contract to have a different deployed bytecode even
|
||||||
|
though the creation bytecode has been the same (which is a requirement because
|
||||||
|
otherwise the address would change). This is due to the fact that the compiler
|
||||||
|
can query external state that might have changed between the two creations
|
||||||
|
and incorporate that into the deployed bytecode before it is stored.
|
||||||
|
|
||||||
|
|
||||||
Order of Evaluation of Expressions
|
Order of Evaluation of Expressions
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ The full contract
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// destroy the contract and reclaim the leftover funds.
|
/// destroy the contract and reclaim the leftover funds.
|
||||||
function kill() public {
|
function shutdown() public {
|
||||||
require(msg.sender == owner);
|
require(msg.sender == owner);
|
||||||
selfdestruct(msg.sender);
|
selfdestruct(msg.sender);
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,12 @@ StructDefinition = 'struct' Identifier '{'
|
|||||||
ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block
|
ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block
|
||||||
ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
|
ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
|
||||||
|
|
||||||
FunctionDefinition = 'function' Identifier? ParameterList
|
FunctionDefinition = FunctionDescriptor ParameterList
|
||||||
( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )*
|
( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )*
|
||||||
( 'returns' ParameterList )? ( ';' | Block )
|
( 'returns' ParameterList )? ( ';' | Block )
|
||||||
|
|
||||||
|
FunctionDescriptor = 'function' Identifier | 'constructor' | 'fallback' | 'receive'
|
||||||
|
|
||||||
OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )?
|
OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )?
|
||||||
|
|
||||||
EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';'
|
EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';'
|
||||||
@ -72,7 +74,7 @@ Statement = IfStatement | TryStatement | WhileStatement | ForStatement | Block |
|
|||||||
ExpressionStatement = Expression
|
ExpressionStatement = Expression
|
||||||
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
|
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
|
||||||
TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+
|
TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+
|
||||||
CatchClause = 'catch' Identifier? ParameterList Block
|
CatchClause = 'catch' ( Identifier? ParameterList )? Block
|
||||||
WhileStatement = 'while' '(' Expression ')' Statement
|
WhileStatement = 'while' '(' Expression ')' Statement
|
||||||
PlaceholderStatement = '_'
|
PlaceholderStatement = '_'
|
||||||
SimpleStatement = VariableDefinition | ExpressionStatement
|
SimpleStatement = VariableDefinition | ExpressionStatement
|
||||||
|
@ -104,4 +104,3 @@ Contents
|
|||||||
common-patterns.rst
|
common-patterns.rst
|
||||||
bugs.rst
|
bugs.rst
|
||||||
contributing.rst
|
contributing.rst
|
||||||
lll.rst
|
|
||||||
|
@ -174,7 +174,8 @@ Install it using ``brew``:
|
|||||||
# eg. Install 0.4.8
|
# eg. Install 0.4.8
|
||||||
brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb
|
brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb
|
||||||
|
|
||||||
Gentoo Linux also provides a solidity package that can be installed using ``emerge``:
|
Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a solidity package.
|
||||||
|
After the overlay is setup, ``solc`` can be installed in x86_64 architectures by:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
|
21
docs/lll.rst
21
docs/lll.rst
@ -1,21 +0,0 @@
|
|||||||
###
|
|
||||||
LLL
|
|
||||||
###
|
|
||||||
|
|
||||||
.. _lll:
|
|
||||||
|
|
||||||
LLL is a low-level language for the EVM with an s-expressions syntax.
|
|
||||||
|
|
||||||
The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity.
|
|
||||||
However, apart from maintaining that it still compiles, no other improvements are made to it.
|
|
||||||
|
|
||||||
It is not built unless specifically requested:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
$ cmake -DLLL=ON ..
|
|
||||||
$ cmake --build .
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
The LLL codebase is deprecated and will be removed from the Solidity repository in the future.
|
|
@ -56,11 +56,11 @@ explanatory purposes.
|
|||||||
// Swarm URL is recommended
|
// Swarm URL is recommended
|
||||||
"urls": [ "bzzr://56ab..." ]
|
"urls": [ "bzzr://56ab..." ]
|
||||||
},
|
},
|
||||||
"mortal": {
|
"destructible": {
|
||||||
// Required: keccak256 hash of the source file
|
// Required: keccak256 hash of the source file
|
||||||
"keccak256": "0x234...",
|
"keccak256": "0x234...",
|
||||||
// Required (unless "url" is used): literal contents of the source file
|
// Required (unless "url" is used): literal contents of the source file
|
||||||
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
|
"content": "contract destructible is owned { function destroy() { if (msg.sender == owner) selfdestruct(owner); } }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Required: Compiler settings
|
// Required: Compiler settings
|
||||||
@ -142,7 +142,7 @@ to the end of the deployed bytecode::
|
|||||||
0xa2
|
0xa2
|
||||||
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
|
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
|
||||||
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
|
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
|
||||||
0x00 0x32
|
0x00 0x33
|
||||||
|
|
||||||
So in order to retrieve the data, the end of the deployed bytecode can be checked
|
So in order to retrieve the data, the end of the deployed bytecode can be checked
|
||||||
to match that pattern and use the IPFS hash to retrieve the file.
|
to match that pattern and use the IPFS hash to retrieve the file.
|
||||||
|
@ -97,23 +97,38 @@ Discontinued:
|
|||||||
Solidity Tools
|
Solidity Tools
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* `ABI to Solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_
|
||||||
|
A script for generating contract interfaces from the ABI of a smart contract.
|
||||||
|
|
||||||
* `Dapp <https://dapp.tools/dapp/>`_
|
* `Dapp <https://dapp.tools/dapp/>`_
|
||||||
Build tool, package manager, and deployment assistant for Solidity.
|
Build tool, package manager, and deployment assistant for Solidity.
|
||||||
|
|
||||||
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
|
||||||
Try Solidity instantly with a command-line Solidity console.
|
|
||||||
|
|
||||||
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
|
||||||
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
|
||||||
|
|
||||||
* `Doxity <https://github.com/DigixGlobal/doxity>`_
|
* `Doxity <https://github.com/DigixGlobal/doxity>`_
|
||||||
Documentation Generator for Solidity.
|
Documentation Generator for Solidity.
|
||||||
|
|
||||||
* `evmdis <https://github.com/Arachnid/evmdis>`_
|
* `evmdis <https://github.com/Arachnid/evmdis>`_
|
||||||
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
|
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
|
||||||
|
|
||||||
* `ABI to solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_
|
* `EVM Lab <https://github.com/ethereum/evmlab/>`_
|
||||||
A script for generating contract interfaces from the ABI of a smart contract.
|
Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display.
|
||||||
|
|
||||||
|
* `leafleth <https://github.com/clemlak/leafleth>`_
|
||||||
|
A documentation generator for Solidity smart-contracts.
|
||||||
|
|
||||||
|
* `PIET <https://piet.slock.it/>`_
|
||||||
|
A tool to develop, audit and use Solidity smart contracts through a simple graphical interface.
|
||||||
|
|
||||||
|
* `solc-select <https://github.com/crytic/solc-select>`_
|
||||||
|
A script to quickly switch between Solidity compiler versions.
|
||||||
|
|
||||||
|
* `Solidity prettier plugin <https://github.com/prettier-solidity/prettier-plugin-solidity>`_
|
||||||
|
A Prettier Plugin for Solidity.
|
||||||
|
|
||||||
|
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
||||||
|
Try Solidity instantly with a command-line Solidity console.
|
||||||
|
|
||||||
|
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
||||||
|
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
||||||
|
|
||||||
* `Securify <https://securify.ch/>`_
|
* `Securify <https://securify.ch/>`_
|
||||||
Fully automated online static analyzer for smart contracts, providing a security report based on vulnerability patterns.
|
Fully automated online static analyzer for smart contracts, providing a security report based on vulnerability patterns.
|
||||||
@ -121,18 +136,9 @@ Solidity Tools
|
|||||||
* `Sūrya <https://github.com/ConsenSys/surya/>`_
|
* `Sūrya <https://github.com/ConsenSys/surya/>`_
|
||||||
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
|
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
|
||||||
|
|
||||||
* `EVM Lab <https://github.com/ethereum/evmlab/>`_
|
|
||||||
Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display.
|
|
||||||
|
|
||||||
* `Universal Mutator <https://github.com/agroce/universalmutator>`_
|
* `Universal Mutator <https://github.com/agroce/universalmutator>`_
|
||||||
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
|
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
|
||||||
|
|
||||||
* `PIET <https://piet.slock.it/>`_
|
|
||||||
A tool to develop, audit and use solidity smart contracts through a simple graphical interface.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
|
|
||||||
|
|
||||||
Third-Party Solidity Parsers and Grammars
|
Third-Party Solidity Parsers and Grammars
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
|||||||
mapping(address => uint) shares;
|
mapping(address => uint) shares;
|
||||||
/// Withdraw your share.
|
/// Withdraw your share.
|
||||||
function withdraw() public {
|
function withdraw() public {
|
||||||
(bool success,) = msg.sender.call.value(shares[msg.sender])("");
|
(bool success,) = msg.sender.call{value: shares[msg.sender]}("");
|
||||||
if (success)
|
if (success)
|
||||||
shares[msg.sender] = 0;
|
shares[msg.sender] = 0;
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ Sending and Receiving Ether
|
|||||||
(for example in the "details" section in Remix).
|
(for example in the "details" section in Remix).
|
||||||
|
|
||||||
- There is a way to forward more gas to the receiving contract using
|
- There is a way to forward more gas to the receiving contract using
|
||||||
``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``,
|
``addr.call{value: x}("")``. This is essentially the same as ``addr.transfer(x)``,
|
||||||
only that it forwards all remaining gas and opens up the ability for the
|
only that it forwards all remaining gas and opens up the ability for the
|
||||||
recipient to perform more expensive actions (and it returns a failure code
|
recipient to perform more expensive actions (and it returns a failure code
|
||||||
instead of automatically propagating the error). This might include calling back
|
instead of automatically propagating the error). This might include calling back
|
||||||
|
@ -584,7 +584,7 @@ Yes::
|
|||||||
return balanceOf[from];
|
return balanceOf[from];
|
||||||
}
|
}
|
||||||
|
|
||||||
function kill() public onlyowner {
|
function shutdown() public onlyowner {
|
||||||
selfdestruct(owner);
|
selfdestruct(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +594,7 @@ No::
|
|||||||
return balanceOf[from];
|
return balanceOf[from];
|
||||||
}
|
}
|
||||||
|
|
||||||
function kill() onlyowner public {
|
function shutdown() onlyowner public {
|
||||||
selfdestruct(owner);
|
selfdestruct(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,17 +276,17 @@ Example::
|
|||||||
arbitrary arguments and would also handle a first argument of type
|
arbitrary arguments and would also handle a first argument of type
|
||||||
``bytes4`` differently. These edge cases were removed in version 0.5.0.
|
``bytes4`` differently. These edge cases were removed in version 0.5.0.
|
||||||
|
|
||||||
It is possible to adjust the supplied gas with the ``.gas()`` modifier::
|
It is possible to adjust the supplied gas with the ``gas`` modifier::
|
||||||
|
|
||||||
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
|
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||||
|
|
||||||
Similarly, the supplied Ether value can be controlled too::
|
Similarly, the supplied Ether value can be controlled too::
|
||||||
|
|
||||||
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
|
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||||
|
|
||||||
Lastly, these modifiers can be combined. Their order does not matter::
|
Lastly, these modifiers can be combined. Their order does not matter::
|
||||||
|
|
||||||
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
|
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||||
|
|
||||||
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
|
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
|
||||||
|
|
||||||
@ -297,7 +297,8 @@ Since byzantium ``staticcall`` can be used as well. This is basically the same a
|
|||||||
|
|
||||||
All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
|
All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
|
||||||
|
|
||||||
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
|
The ``gas`` option is available on all three methods, while the ``value`` option is not
|
||||||
|
supported for ``delegatecall``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||||
@ -635,8 +636,12 @@ External (or public) functions have the following members:
|
|||||||
|
|
||||||
* ``.address`` returns the address of the contract of the function.
|
* ``.address`` returns the address of the contract of the function.
|
||||||
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>`
|
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>`
|
||||||
* ``.gas(uint)`` returns a callable function object which, when called, will send the specified amount of gas to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
|
* ``.gas(uint)`` returns a callable function object which, when called, will send
|
||||||
* ``.value(uint)`` returns a callable function object which, when called, will send the specified amount of wei to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
|
the specified amount of gas to the target function. Deprecated - use ``{gas: ...}`` instead.
|
||||||
|
See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||||
|
* ``.value(uint)`` returns a callable function object which, when called, will
|
||||||
|
send the specified amount of wei to the target function. Deprecated - use ``{value: ...}`` instead.
|
||||||
|
See :ref:`External Function Calls <external-function-calls>` for more information.
|
||||||
|
|
||||||
Example that shows how to use the members::
|
Example that shows how to use the members::
|
||||||
|
|
||||||
@ -651,6 +656,8 @@ Example that shows how to use the members::
|
|||||||
|
|
||||||
function g() public {
|
function g() public {
|
||||||
this.f.gas(10).value(800)();
|
this.f.gas(10).value(800)();
|
||||||
|
// New syntax:
|
||||||
|
// this.f{gas: 10, value: 800}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,12 +180,12 @@ Input Description
|
|||||||
// `--allow-paths <path>`.
|
// `--allow-paths <path>`.
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mortal":
|
"destructible":
|
||||||
{
|
{
|
||||||
// Optional: keccak256 hash of the source file
|
// Optional: keccak256 hash of the source file
|
||||||
"keccak256": "0x234...",
|
"keccak256": "0x234...",
|
||||||
// Required (unless "urls" is used): literal contents of the source file
|
// Required (unless "urls" is used): literal contents of the source file
|
||||||
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
|
"content": "contract destructible is owned { function shutdown() { if (msg.sender == owner) selfdestruct(owner); } }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Optional
|
// Optional
|
||||||
@ -220,8 +220,11 @@ Input Description
|
|||||||
"cse": false,
|
"cse": false,
|
||||||
// Optimize representation of literal numbers and strings in code.
|
// Optimize representation of literal numbers and strings in code.
|
||||||
"constantOptimizer": false,
|
"constantOptimizer": false,
|
||||||
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2.
|
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2
|
||||||
// It can only be activated through the details here.
|
// and inline assembly.
|
||||||
|
// It is activated together with the global optimizer setting
|
||||||
|
// and can be deactivated here.
|
||||||
|
// Before Solidity 0.6.0 it had to be activated through this switch.
|
||||||
"yul": false,
|
"yul": false,
|
||||||
// Tuning options for the Yul optimizer.
|
// Tuning options for the Yul optimizer.
|
||||||
"yulDetails": {
|
"yulDetails": {
|
||||||
|
948
docs/yul.rst
948
docs/yul.rst
File diff suppressed because it is too large
Load Diff
@ -33,51 +33,10 @@
|
|||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
using namespace solidity::util;
|
||||||
void Assembly::append(Assembly const& _a)
|
|
||||||
{
|
|
||||||
auto newDeposit = m_deposit + _a.deposit();
|
|
||||||
for (AssemblyItem i: _a.m_items)
|
|
||||||
{
|
|
||||||
switch (i.type())
|
|
||||||
{
|
|
||||||
case Tag:
|
|
||||||
case PushTag:
|
|
||||||
i.setData(i.data() + m_usedTags);
|
|
||||||
break;
|
|
||||||
case PushSub:
|
|
||||||
case PushSubSize:
|
|
||||||
i.setData(i.data() + m_subs.size());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
append(i);
|
|
||||||
}
|
|
||||||
m_deposit = newDeposit;
|
|
||||||
m_usedTags += _a.m_usedTags;
|
|
||||||
// This does not transfer the names of named tags on purpose. The tags themselves are
|
|
||||||
// transferred, but their names are only available inside the assembly.
|
|
||||||
for (auto const& i: _a.m_data)
|
|
||||||
m_data.insert(i);
|
|
||||||
for (auto const& i: _a.m_strings)
|
|
||||||
m_strings.insert(i);
|
|
||||||
m_subs += _a.m_subs;
|
|
||||||
for (auto const& lib: _a.m_libraries)
|
|
||||||
m_libraries.insert(lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Assembly::append(Assembly const& _a, int _deposit)
|
|
||||||
{
|
|
||||||
assertThrow(_deposit <= _a.m_deposit, InvalidDeposit, "");
|
|
||||||
|
|
||||||
append(_a);
|
|
||||||
while (_deposit++ < _a.m_deposit)
|
|
||||||
append(Instruction::POP);
|
|
||||||
}
|
|
||||||
|
|
||||||
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
||||||
{
|
{
|
||||||
@ -87,12 +46,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
|||||||
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
||||||
m_items.back().setLocation(m_currentSourceLocation);
|
m_items.back().setLocation(m_currentSourceLocation);
|
||||||
m_items.back().m_modifierDepth = m_currentModifierDepth;
|
m_items.back().m_modifierDepth = m_currentModifierDepth;
|
||||||
return back();
|
return m_items.back();
|
||||||
}
|
|
||||||
|
|
||||||
void Assembly::injectStart(AssemblyItem const& _i)
|
|
||||||
{
|
|
||||||
m_items.insert(m_items.begin(), _i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Assembly::bytesRequired(unsigned subTagSize) const
|
unsigned Assembly::bytesRequired(unsigned subTagSize) const
|
||||||
@ -105,7 +59,7 @@ unsigned Assembly::bytesRequired(unsigned subTagSize) const
|
|||||||
|
|
||||||
for (AssemblyItem const& i: m_items)
|
for (AssemblyItem const& i: m_items)
|
||||||
ret += i.bytesRequired(tagSize);
|
ret += i.bytesRequired(tagSize);
|
||||||
if (dev::bytesRequired(ret) <= tagSize)
|
if (util::bytesRequired(ret) <= tagSize)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,15 +244,15 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
|||||||
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, ""));
|
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, ""));
|
||||||
else
|
else
|
||||||
collection.append(
|
collection.append(
|
||||||
createJsonValue("PUSH [tag]", i.location().start, i.location().end, dev::toString(i.data())));
|
createJsonValue("PUSH [tag]", i.location().start, i.location().end, toString(i.data())));
|
||||||
break;
|
break;
|
||||||
case PushSub:
|
case PushSub:
|
||||||
collection.append(
|
collection.append(
|
||||||
createJsonValue("PUSH [$]", i.location().start, i.location().end, dev::toString(h256(i.data()))));
|
createJsonValue("PUSH [$]", i.location().start, i.location().end, toString(h256(i.data()))));
|
||||||
break;
|
break;
|
||||||
case PushSubSize:
|
case PushSubSize:
|
||||||
collection.append(
|
collection.append(
|
||||||
createJsonValue("PUSH #[$]", i.location().start, i.location().end, dev::toString(h256(i.data()))));
|
createJsonValue("PUSH #[$]", i.location().start, i.location().end, toString(h256(i.data()))));
|
||||||
break;
|
break;
|
||||||
case PushProgramSize:
|
case PushProgramSize:
|
||||||
collection.append(
|
collection.append(
|
||||||
@ -316,7 +270,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
|||||||
break;
|
break;
|
||||||
case Tag:
|
case Tag:
|
||||||
collection.append(
|
collection.append(
|
||||||
createJsonValue("tag", i.location().start, i.location().end, dev::toString(i.data())));
|
createJsonValue("tag", i.location().start, i.location().end, toString(i.data())));
|
||||||
collection.append(
|
collection.append(
|
||||||
createJsonValue("JUMPDEST", i.location().start, i.location().end));
|
createJsonValue("JUMPDEST", i.location().start, i.location().end));
|
||||||
break;
|
break;
|
||||||
@ -359,7 +313,7 @@ AssemblyItem Assembly::namedTag(string const& _name)
|
|||||||
|
|
||||||
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||||
{
|
{
|
||||||
h256 h(dev::keccak256(_identifier));
|
h256 h(util::keccak256(_identifier));
|
||||||
m_libraries[h] = _identifier;
|
m_libraries[h] = _identifier;
|
||||||
return AssemblyItem{PushLibraryAddress, h};
|
return AssemblyItem{PushLibraryAddress, h};
|
||||||
}
|
}
|
||||||
@ -543,14 +497,14 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
multimap<h256, unsigned> dataRef;
|
multimap<h256, unsigned> dataRef;
|
||||||
multimap<size_t, size_t> subRef;
|
multimap<size_t, size_t> subRef;
|
||||||
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
|
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
|
||||||
unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode);
|
unsigned bytesPerTag = util::bytesRequired(bytesRequiredForCode);
|
||||||
uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag;
|
uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag;
|
||||||
|
|
||||||
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size();
|
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size();
|
||||||
for (auto const& sub: m_subs)
|
for (auto const& sub: m_subs)
|
||||||
bytesRequiredIncludingData += sub->assemble().bytecode.size();
|
bytesRequiredIncludingData += sub->assemble().bytecode.size();
|
||||||
|
|
||||||
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
|
unsigned bytesPerDataRef = util::bytesRequired(bytesRequiredIncludingData);
|
||||||
uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
||||||
ret.bytecode.reserve(bytesRequiredIncludingData);
|
ret.bytecode.reserve(bytesRequiredIncludingData);
|
||||||
|
|
||||||
@ -580,7 +534,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
}
|
}
|
||||||
case Push:
|
case Push:
|
||||||
{
|
{
|
||||||
uint8_t b = max<unsigned>(1, dev::bytesRequired(i.data()));
|
uint8_t b = max<unsigned>(1, util::bytesRequired(i.data()));
|
||||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
||||||
ret.bytecode.resize(ret.bytecode.size() + b);
|
ret.bytecode.resize(ret.bytecode.size() + b);
|
||||||
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
||||||
@ -610,7 +564,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
assertThrow(i.data() <= size_t(-1), AssemblyException, "");
|
assertThrow(i.data() <= size_t(-1), AssemblyException, "");
|
||||||
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
|
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
|
||||||
i.setPushedValue(u256(s));
|
i.setPushedValue(u256(s));
|
||||||
uint8_t b = max<unsigned>(1, dev::bytesRequired(s));
|
uint8_t b = max<unsigned>(1, util::bytesRequired(s));
|
||||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
||||||
ret.bytecode.resize(ret.bytecode.size() + b);
|
ret.bytecode.resize(ret.bytecode.size() + b);
|
||||||
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
||||||
@ -675,7 +629,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag.");
|
assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag.");
|
||||||
size_t pos = tagPositions[tagId];
|
size_t pos = tagPositions[tagId];
|
||||||
assertThrow(pos != size_t(-1), AssemblyException, "Reference to tag without position.");
|
assertThrow(pos != size_t(-1), AssemblyException, "Reference to tag without position.");
|
||||||
assertThrow(dev::bytesRequired(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space.");
|
assertThrow(util::bytesRequired(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space.");
|
||||||
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
|
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
|
||||||
toBigEndian(pos, r);
|
toBigEndian(pos, r);
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
@ -35,9 +35,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
using AssemblyPointer = std::shared_ptr<Assembly>;
|
using AssemblyPointer = std::shared_ptr<Assembly>;
|
||||||
@ -49,8 +47,8 @@ public:
|
|||||||
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
||||||
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
||||||
AssemblyItem namedTag(std::string const& _name);
|
AssemblyItem namedTag(std::string const& _name);
|
||||||
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
||||||
bytes const& data(h256 const& _i) const { return m_data.at(_i); }
|
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
|
||||||
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
||||||
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||||
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||||
@ -58,7 +56,6 @@ public:
|
|||||||
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
||||||
|
|
||||||
AssemblyItem const& append(AssemblyItem const& _i);
|
AssemblyItem const& append(AssemblyItem const& _i);
|
||||||
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
|
|
||||||
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
||||||
|
|
||||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||||
@ -139,18 +136,6 @@ public:
|
|||||||
StringMap const& _sourceCodes = StringMap()
|
StringMap const& _sourceCodes = StringMap()
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
public:
|
|
||||||
// These features are only used by LLL
|
|
||||||
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
|
|
||||||
|
|
||||||
void append(Assembly const& _a);
|
|
||||||
void append(Assembly const& _a, int _deposit);
|
|
||||||
|
|
||||||
void injectStart(AssemblyItem const& _i);
|
|
||||||
|
|
||||||
AssemblyItem const& back() const { return m_items.back(); }
|
|
||||||
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||||
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
|
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
|
||||||
@ -168,12 +153,12 @@ protected:
|
|||||||
unsigned m_usedTags = 1;
|
unsigned m_usedTags = 1;
|
||||||
std::map<std::string, size_t> m_namedTags;
|
std::map<std::string, size_t> m_namedTags;
|
||||||
AssemblyItems m_items;
|
AssemblyItems m_items;
|
||||||
std::map<h256, bytes> m_data;
|
std::map<util::h256, bytes> m_data;
|
||||||
/// Data that is appended to the very end of the contract.
|
/// Data that is appended to the very end of the contract.
|
||||||
bytes m_auxiliaryData;
|
bytes m_auxiliaryData;
|
||||||
std::vector<std::shared_ptr<Assembly>> m_subs;
|
std::vector<std::shared_ptr<Assembly>> m_subs;
|
||||||
std::map<h256, std::string> m_strings;
|
std::map<util::h256, std::string> m_strings;
|
||||||
std::map<h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||||
|
|
||||||
mutable LinkerObject m_assembledObject;
|
mutable LinkerObject m_assembledObject;
|
||||||
mutable std::vector<size_t> m_tagPositionsInBytecode;
|
mutable std::vector<size_t> m_tagPositionsInBytecode;
|
||||||
@ -192,4 +177,3 @@ inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -17,21 +17,21 @@
|
|||||||
|
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libdevcore/FixedHash.h>
|
#include <libsolutil/FixedHash.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
|
static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
|
||||||
|
|
||||||
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
|
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
|
||||||
{
|
{
|
||||||
assertThrow(data() < (u256(1) << 64), Exception, "Tag already has subassembly set.");
|
assertThrow(data() < (u256(1) << 64), util::Exception, "Tag already has subassembly set.");
|
||||||
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
|
||||||
size_t tag = size_t(u256(data()) & 0xffffffffffffffffULL);
|
size_t tag = size_t(u256(data()) & 0xffffffffffffffffULL);
|
||||||
AssemblyItem r = *this;
|
AssemblyItem r = *this;
|
||||||
r.m_type = PushTag;
|
r.m_type = PushTag;
|
||||||
@ -41,7 +41,7 @@ AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
|
|||||||
|
|
||||||
pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
|
pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
|
||||||
{
|
{
|
||||||
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
|
||||||
u256 combined = u256(data());
|
u256 combined = u256(data());
|
||||||
size_t subId = size_t((combined >> 64) - 1);
|
size_t subId = size_t((combined >> 64) - 1);
|
||||||
size_t tag = size_t(combined & 0xffffffffffffffffULL);
|
size_t tag = size_t(combined & 0xffffffffffffffffULL);
|
||||||
@ -50,7 +50,7 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
|
|||||||
|
|
||||||
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
|
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
|
||||||
{
|
{
|
||||||
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
|
||||||
u256 data = _tag;
|
u256 data = _tag;
|
||||||
if (_subId != size_t(-1))
|
if (_subId != size_t(-1))
|
||||||
data |= (u256(_subId) + 1) << 64;
|
data |= (u256(_subId) + 1) << 64;
|
||||||
@ -67,7 +67,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
|
|||||||
case PushString:
|
case PushString:
|
||||||
return 1 + 32;
|
return 1 + 32;
|
||||||
case Push:
|
case Push:
|
||||||
return 1 + max<unsigned>(1, dev::bytesRequired(data()));
|
return 1 + max<unsigned>(1, util::bytesRequired(data()));
|
||||||
case PushSubSize:
|
case PushSubSize:
|
||||||
case PushProgramSize:
|
case PushProgramSize:
|
||||||
return 1 + 4; // worst case: a 16MB program
|
return 1 + 4; // worst case: a 16MB program
|
||||||
@ -170,10 +170,10 @@ string AssemblyItem::toAssemblyText() const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Push:
|
case Push:
|
||||||
text = toHex(toCompactBigEndian(data(), 1), HexPrefix::Add);
|
text = toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add);
|
||||||
break;
|
break;
|
||||||
case PushString:
|
case PushString:
|
||||||
text = string("data_") + toHex(data());
|
text = string("data_") + util::toHex(data());
|
||||||
break;
|
break;
|
||||||
case PushTag:
|
case PushTag:
|
||||||
{
|
{
|
||||||
@ -191,7 +191,7 @@ string AssemblyItem::toAssemblyText() const
|
|||||||
text = string("tag_") + to_string(size_t(data())) + ":";
|
text = string("tag_") + to_string(size_t(data())) + ":";
|
||||||
break;
|
break;
|
||||||
case PushData:
|
case PushData:
|
||||||
text = string("data_") + toHex(data());
|
text = string("data_") + util::toHex(data());
|
||||||
break;
|
break;
|
||||||
case PushSub:
|
case PushSub:
|
||||||
text = string("dataOffset(sub_") + to_string(size_t(data())) + ")";
|
text = string("dataOffset(sub_") + to_string(size_t(data())) + ")";
|
||||||
@ -203,7 +203,7 @@ string AssemblyItem::toAssemblyText() const
|
|||||||
text = string("bytecodeSize");
|
text = string("bytecodeSize");
|
||||||
break;
|
break;
|
||||||
case PushLibraryAddress:
|
case PushLibraryAddress:
|
||||||
text = string("linkerSymbol(\"") + toHex(data()) + string("\")");
|
text = string("linkerSymbol(\"") + util::toHex(data()) + string("\")");
|
||||||
break;
|
break;
|
||||||
case PushDeployTimeAddress:
|
case PushDeployTimeAddress:
|
||||||
text = string("deployTimeAddress()");
|
text = string("deployTimeAddress()");
|
||||||
@ -225,7 +225,7 @@ string AssemblyItem::toAssemblyText() const
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
||||||
{
|
{
|
||||||
switch (_item.type())
|
switch (_item.type())
|
||||||
{
|
{
|
||||||
@ -266,7 +266,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
break;
|
break;
|
||||||
case PushLibraryAddress:
|
case PushLibraryAddress:
|
||||||
{
|
{
|
||||||
string hash(h256((_item.data())).hex());
|
string hash(util::h256((_item.data())).hex());
|
||||||
_out << " PushLibraryAddress " << hash.substr(0, 8) + "..." + hash.substr(hash.length() - 8);
|
_out << " PushLibraryAddress " << hash.substr(0, 8) + "..." + hash.substr(hash.length() - 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,12 @@
|
|||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
enum AssemblyItemType {
|
enum AssemblyItemType {
|
||||||
@ -77,8 +75,8 @@ public:
|
|||||||
AssemblyItem& operator=(AssemblyItem const&) = default;
|
AssemblyItem& operator=(AssemblyItem const&) = default;
|
||||||
AssemblyItem& operator=(AssemblyItem&&) = default;
|
AssemblyItem& operator=(AssemblyItem&&) = default;
|
||||||
|
|
||||||
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, data()); }
|
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); }
|
||||||
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
|
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); }
|
||||||
/// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies.
|
/// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies.
|
||||||
/// @param _subId the identifier of the subassembly the tag is taken from.
|
/// @param _subId the identifier of the subassembly the tag is taken from.
|
||||||
AssemblyItem toSubAssemblyTag(size_t _subId) const;
|
AssemblyItem toSubAssemblyTag(size_t _subId) const;
|
||||||
@ -89,11 +87,11 @@ public:
|
|||||||
void setPushTagSubIdAndTag(size_t _subId, size_t _tag);
|
void setPushTagSubIdAndTag(size_t _subId, size_t _tag);
|
||||||
|
|
||||||
AssemblyItemType type() const { return m_type; }
|
AssemblyItemType type() const { return m_type; }
|
||||||
u256 const& data() const { assertThrow(m_type != Operation, Exception, ""); return *m_data; }
|
u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
|
||||||
void setData(u256 const& _data) { assertThrow(m_type != Operation, Exception, ""); m_data = std::make_shared<u256>(_data); }
|
void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); }
|
||||||
|
|
||||||
/// @returns the instruction of this item (only valid if type() == Operation)
|
/// @returns the instruction of this item (only valid if type() == Operation)
|
||||||
Instruction instruction() const { assertThrow(m_type == Operation, Exception, ""); return m_instruction; }
|
Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; }
|
||||||
|
|
||||||
/// @returns true if the type and data of the items are equal.
|
/// @returns true if the type and data of the items are equal.
|
||||||
bool operator==(AssemblyItem const& _other) const
|
bool operator==(AssemblyItem const& _other) const
|
||||||
@ -178,4 +176,3 @@ inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
|
|
||||||
bool BlockDeduplicator::deduplicate()
|
bool BlockDeduplicator::deduplicate()
|
||||||
|
@ -23,16 +23,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
@ -90,4 +88,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -36,4 +36,4 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(evmasm ${sources})
|
add_library(evmasm ${sources})
|
||||||
target_link_libraries(evmasm PUBLIC devcore)
|
target_link_libraries(evmasm PUBLIC solutil)
|
||||||
|
@ -23,14 +23,14 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
|
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
|
||||||
{
|
{
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
#include <libdevcore/Exceptions.h>
|
#include <libsolutil/Exceptions.h>
|
||||||
#include <libevmasm/ExpressionClasses.h>
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
#include <libevmasm/KnownState.h>
|
#include <libevmasm/KnownState.h>
|
||||||
@ -39,9 +39,7 @@ namespace langutil
|
|||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
@ -187,4 +185,3 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -22,9 +22,11 @@
|
|||||||
#include <libevmasm/ConstantOptimiser.h>
|
#include <libevmasm/ConstantOptimiser.h>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
unsigned ConstantOptimisationMethod::optimiseConstants(
|
unsigned ConstantOptimisationMethod::optimiseConstants(
|
||||||
bool _isCreation,
|
bool _isCreation,
|
||||||
@ -101,7 +103,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
|
|||||||
|
|
||||||
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
||||||
{
|
{
|
||||||
return eth::bytesRequired(_items, 3); // assume 3 byte addresses
|
return evmasm::bytesRequired(_items, 3); // assume 3 byte addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstantOptimisationMethod::replaceConstants(
|
void ConstantOptimisationMethod::replaceConstants(
|
||||||
@ -131,7 +133,7 @@ bigint LiteralMethod::gasNeeded() const
|
|||||||
return combineGas(
|
return combineGas(
|
||||||
simpleRunGas({Instruction::PUSH1}),
|
simpleRunGas({Instruction::PUSH1}),
|
||||||
// PUSHX plus data
|
// PUSHX plus data
|
||||||
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
|
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(util::toCompactBigEndian(m_value, 1)),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -144,13 +146,13 @@ bigint CodeCopyMethod::gasNeeded() const
|
|||||||
// Data gas for copy routines: Some bytes are zero, but we ignore them.
|
// Data gas for copy routines: Some bytes are zero, but we ignore them.
|
||||||
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
|
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
|
||||||
// Data gas for data itself
|
// Data gas for data itself
|
||||||
dataGas(toBigEndian(m_value))
|
dataGas(util::toBigEndian(m_value))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const
|
AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const
|
||||||
{
|
{
|
||||||
bytes data = toBigEndian(m_value);
|
bytes data = util::toBigEndian(m_value);
|
||||||
assertThrow(data.size() == 32, OptimizerException, "Invalid number encoding.");
|
assertThrow(data.size() == 32, OptimizerException, "Invalid number encoding.");
|
||||||
AssemblyItems actualCopyRoutine = copyRoutine();
|
AssemblyItems actualCopyRoutine = copyRoutine();
|
||||||
actualCopyRoutine[4] = _assembly.newData(data);
|
actualCopyRoutine[4] = _assembly.newData(data);
|
||||||
@ -190,7 +192,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
|
|||||||
if (_value < 0x10000)
|
if (_value < 0x10000)
|
||||||
// Very small value, not worth computing
|
// Very small value, not worth computing
|
||||||
return AssemblyItems{_value};
|
return AssemblyItems{_value};
|
||||||
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value))
|
else if (util::bytesRequired(~_value) < util::bytesRequired(_value))
|
||||||
// Negated is shorter to represent
|
// Negated is shorter to represent
|
||||||
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
|
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
|
||||||
else
|
else
|
||||||
|
@ -25,15 +25,13 @@
|
|||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
@ -164,4 +162,3 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
#include <libevmasm/KnownState.h>
|
#include <libevmasm/KnownState.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
BlockId::BlockId(u256 const& _id):
|
BlockId::BlockId(u256 const& _id):
|
||||||
m_id(unsigned(_id))
|
m_id(unsigned(_id))
|
||||||
|
@ -25,13 +25,11 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <libevmasm/ExpressionClasses.h>
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class KnownState;
|
class KnownState;
|
||||||
@ -127,4 +125,3 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -21,14 +21,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Exceptions.h>
|
#include <libsolutil/Exceptions.h>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
struct AssemblyException: virtual Exception {};
|
struct AssemblyException: virtual util::Exception {};
|
||||||
struct OptimizerException: virtual AssemblyException {};
|
struct OptimizerException: virtual AssemblyException {};
|
||||||
struct StackTooDeepException: virtual OptimizerException {};
|
struct StackTooDeepException: virtual OptimizerException {};
|
||||||
struct ItemNotAvailableException: virtual OptimizerException {};
|
struct ItemNotAvailableException: virtual OptimizerException {};
|
||||||
@ -37,4 +35,3 @@ DEV_SIMPLE_EXCEPTION(InvalidDeposit);
|
|||||||
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
|
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -32,9 +32,9 @@
|
|||||||
#include <libevmasm/SimplificationRules.h>
|
#include <libevmasm/SimplificationRules.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
|
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -31,14 +31,12 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class Pattern;
|
class Pattern;
|
||||||
@ -128,4 +126,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -19,11 +19,10 @@
|
|||||||
|
|
||||||
#include <libevmasm/KnownState.h>
|
#include <libevmasm/KnownState.h>
|
||||||
|
|
||||||
#include <libdevcore/FixedHash.h>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
|
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
|
||||||
{
|
{
|
||||||
@ -181,7 +180,14 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
|||||||
case Instruction::EXP:
|
case Instruction::EXP:
|
||||||
gas = GasCosts::expGas;
|
gas = GasCosts::expGas;
|
||||||
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
||||||
gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8));
|
{
|
||||||
|
if (*value)
|
||||||
|
{
|
||||||
|
// Note: msb() counts from 0 and throws on 0 as input.
|
||||||
|
unsigned const significantByteCount = (boost::multiprecision::msb(*value) + 1 + 7) / 8;
|
||||||
|
gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
gas += GasCosts::expByteGas(m_evmVersion) * 32;
|
gas += GasCosts::expByteGas(m_evmVersion) * 32;
|
||||||
break;
|
break;
|
||||||
|
@ -29,9 +29,7 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class KnownState;
|
class KnownState;
|
||||||
@ -180,4 +178,3 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -21,16 +21,17 @@
|
|||||||
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
std::map<std::string, Instruction> const dev::eth::c_instructions =
|
std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
|
||||||
{
|
{
|
||||||
{ "STOP", Instruction::STOP },
|
{ "STOP", Instruction::STOP },
|
||||||
{ "ADD", Instruction::ADD },
|
{ "ADD", Instruction::ADD },
|
||||||
@ -322,7 +323,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
|
|||||||
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
|
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
|
||||||
};
|
};
|
||||||
|
|
||||||
void dev::eth::eachInstruction(
|
void solidity::evmasm::eachInstruction(
|
||||||
bytes const& _mem,
|
bytes const& _mem,
|
||||||
function<void(Instruction,u256 const&)> const& _onInstruction
|
function<void(Instruction,u256 const&)> const& _onInstruction
|
||||||
)
|
)
|
||||||
@ -351,7 +352,7 @@ void dev::eth::eachInstruction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string dev::eth::disassemble(bytes const& _mem)
|
string solidity::evmasm::disassemble(bytes const& _mem)
|
||||||
{
|
{
|
||||||
stringstream ret;
|
stringstream ret;
|
||||||
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
|
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
|
||||||
@ -368,7 +369,7 @@ string dev::eth::disassemble(bytes const& _mem)
|
|||||||
return ret.str();
|
return ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionInfo dev::eth::instructionInfo(Instruction _inst)
|
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -380,7 +381,7 @@ InstructionInfo dev::eth::instructionInfo(Instruction _inst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dev::eth::isValidInstruction(Instruction _inst)
|
bool solidity::evmasm::isValidInstruction(Instruction _inst)
|
||||||
{
|
{
|
||||||
return !!c_instructionInfo.count(_inst);
|
return !!c_instructionInfo.count(_inst);
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Virtual machine bytecode instruction.
|
/// Virtual machine bytecode instruction.
|
||||||
@ -318,4 +316,3 @@ void eachInstruction(bytes const& _mem, std::function<void(Instruction,u256 cons
|
|||||||
std::string disassemble(bytes const& _mem);
|
std::string disassemble(bytes const& _mem);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev::eth;
|
using namespace solidity;
|
||||||
using namespace dev;
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
bool JumpdestRemover::optimise(set<size_t> const& _tagsReferencedFromOutside)
|
bool JumpdestRemover::optimise(set<size_t> const& _tagsReferencedFromOutside)
|
||||||
{
|
{
|
||||||
|
@ -24,9 +24,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
using AssemblyItems = std::vector<AssemblyItem>;
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
@ -47,4 +45,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -23,14 +23,14 @@
|
|||||||
|
|
||||||
#include <libevmasm/KnownState.h>
|
#include <libevmasm/KnownState.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
ostream& KnownState::stream(ostream& _out) const
|
ostream& KnownState::stream(ostream& _out) const
|
||||||
{
|
{
|
||||||
@ -383,9 +383,9 @@ KnownState::Id KnownState::applyKeccak256(
|
|||||||
{
|
{
|
||||||
bytes data;
|
bytes data;
|
||||||
for (Id a: arguments)
|
for (Id a: arguments)
|
||||||
data += toBigEndian(*m_expressionClasses->knownConstant(a));
|
data += util::toBigEndian(*m_expressionClasses->knownConstant(a));
|
||||||
data.resize(size_t(*l));
|
data.resize(size_t(*l));
|
||||||
v = m_expressionClasses->find(AssemblyItem(u256(dev::keccak256(data)), _location));
|
v = m_expressionClasses->find(AssemblyItem(u256(util::keccak256(data)), _location));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
v = m_expressionClasses->find(keccak256Item, {_start, _length}, true, m_sequenceNumber);
|
v = m_expressionClasses->find(keccak256Item, {_start, _length}, true, m_sequenceNumber);
|
||||||
|
@ -41,19 +41,17 @@
|
|||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
#endif // defined(__clang__)
|
#endif // defined(__clang__)
|
||||||
|
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
#include <libdevcore/Exceptions.h>
|
#include <libsolutil/Exceptions.h>
|
||||||
#include <libevmasm/ExpressionClasses.h>
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
@ -182,4 +180,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -20,12 +20,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libdevcore/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
|
||||||
using namespace dev;
|
|
||||||
using namespace dev::eth;
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
void LinkerObject::append(LinkerObject const& _other)
|
void LinkerObject::append(LinkerObject const& _other)
|
||||||
{
|
{
|
||||||
@ -47,7 +48,7 @@ void LinkerObject::link(map<string, h160> const& _libraryAddresses)
|
|||||||
|
|
||||||
string LinkerObject::toHex() const
|
string LinkerObject::toHex() const
|
||||||
{
|
{
|
||||||
string hex = dev::toHex(bytecode);
|
string hex = solidity::util::toHex(bytecode);
|
||||||
for (auto const& ref: linkReferences)
|
for (auto const& ref: linkReferences)
|
||||||
{
|
{
|
||||||
size_t pos = ref.first * 2;
|
size_t pos = ref.first * 2;
|
||||||
|
@ -21,12 +21,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/FixedHash.h>
|
#include <libsolutil/FixedHash.h>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +44,7 @@ struct LinkerObject
|
|||||||
void append(LinkerObject const& _other);
|
void append(LinkerObject const& _other);
|
||||||
|
|
||||||
/// Links the given libraries by replacing their uses in the code and removes them from the references.
|
/// Links the given libraries by replacing their uses in the code and removes them from the references.
|
||||||
void link(std::map<std::string, h160> const& _libraryAddresses);
|
void link(std::map<std::string, util::h160> const& _libraryAddresses);
|
||||||
|
|
||||||
/// @returns a hex representation of the bytecode of the given object, replacing unlinked
|
/// @returns a hex representation of the bytecode of the given object, replacing unlinked
|
||||||
/// addresses by placeholders. This output is lowercase.
|
/// addresses by placeholders. This output is lowercase.
|
||||||
@ -58,11 +56,10 @@ struct LinkerObject
|
|||||||
static std::string libraryPlaceholder(std::string const& _libraryName);
|
static std::string libraryPlaceholder(std::string const& _libraryName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static h160 const* matchLibrary(
|
static util::h160 const* matchLibrary(
|
||||||
std::string const& _linkRefName,
|
std::string const& _linkRefName,
|
||||||
std::map<std::string, h160> const& _libraryAddresses
|
std::map<std::string, util::h160> const& _libraryAddresses
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
PathGasMeter::PathGasMeter(AssemblyItems const& _items, langutil::EVMVersion _evmVersion):
|
PathGasMeter::PathGasMeter(AssemblyItems const& _items, langutil::EVMVersion _evmVersion):
|
||||||
m_items(_items), m_evmVersion(_evmVersion)
|
m_items(_items), m_evmVersion(_evmVersion)
|
||||||
|
@ -29,9 +29,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class KnownState;
|
class KnownState;
|
||||||
@ -85,4 +83,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev::eth;
|
using namespace solidity;
|
||||||
using namespace dev;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
// TODO: Extend this to use the tools from ExpressionClasses.cpp
|
// TODO: Extend this to use the tools from ExpressionClasses.cpp
|
||||||
|
|
||||||
@ -362,7 +362,7 @@ bool PeepholeOptimiser::optimise()
|
|||||||
);
|
);
|
||||||
if (m_optimisedItems.size() < m_items.size() || (
|
if (m_optimisedItems.size() < m_items.size() || (
|
||||||
m_optimisedItems.size() == m_items.size() && (
|
m_optimisedItems.size() == m_items.size() && (
|
||||||
eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) ||
|
evmasm::bytesRequired(m_optimisedItems, 3) < evmasm::bytesRequired(m_items, 3) ||
|
||||||
numberOfPops(m_optimisedItems) > numberOfPops(m_items)
|
numberOfPops(m_optimisedItems) > numberOfPops(m_items)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
@ -24,9 +24,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
using AssemblyItems = std::vector<AssemblyItem>;
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
@ -53,4 +51,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -25,16 +25,14 @@
|
|||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/SimplificationRule.h>
|
#include <libevmasm/SimplificationRule.h>
|
||||||
|
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/multiprecision/detail/min_max.hpp>
|
#include <boost/multiprecision/detail/min_max.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
template <class S> S divWorkaround(S const& _a, S const& _b)
|
template <class S> S divWorkaround(S const& _a, S const& _b)
|
||||||
@ -674,4 +672,3 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
|
||||||
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
|
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
|
||||||
{
|
{
|
||||||
@ -96,14 +96,14 @@ bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
|
|||||||
{
|
{
|
||||||
if (_item.type() != Operation)
|
if (_item.type() != Operation)
|
||||||
return false;
|
return false;
|
||||||
return dev::eth::isDupInstruction(_item.instruction());
|
return evmasm::isDupInstruction(_item.instruction());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
|
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
|
||||||
{
|
{
|
||||||
if (_item.type() != Operation)
|
if (_item.type() != Operation)
|
||||||
return false;
|
return false;
|
||||||
return dev::eth::isSwapInstruction(_item.instruction());
|
return evmasm::isSwapInstruction(_item.instruction());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
|
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
|
||||||
|
@ -25,9 +25,7 @@
|
|||||||
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class AssemblyItem;
|
class AssemblyItem;
|
||||||
@ -75,4 +73,3 @@ struct SemanticInformation
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -21,12 +21,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +65,7 @@ struct EVMBuiltins
|
|||||||
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
||||||
{
|
{
|
||||||
return {inst, {std::forward<Args>(_args)...}};
|
return {inst, {std::forward<Args>(_args)...}};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PatternGeneratorInstance
|
struct PatternGeneratorInstance
|
||||||
@ -76,7 +74,7 @@ struct EVMBuiltins
|
|||||||
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
||||||
{
|
{
|
||||||
return {instruction, {std::forward<Args>(_args)...}};
|
return {instruction, {std::forward<Args>(_args)...}};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -158,4 +156,3 @@ struct EVMBuiltins
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/RuleList.h>
|
#include <libevmasm/RuleList.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
@ -36,9 +36,9 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
SimplificationRule<Pattern> const* Rules::findFirstMatch(
|
SimplificationRule<Pattern> const* Rules::findFirstMatch(
|
||||||
Expression const& _expr,
|
Expression const& _expr,
|
||||||
|
@ -26,21 +26,19 @@
|
|||||||
#include <libevmasm/ExpressionClasses.h>
|
#include <libevmasm/ExpressionClasses.h>
|
||||||
#include <libevmasm/SimplificationRule.h>
|
#include <libevmasm/SimplificationRule.h>
|
||||||
|
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::evmasm
|
||||||
{
|
|
||||||
namespace eth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class Pattern;
|
class Pattern;
|
||||||
@ -89,7 +87,7 @@ public:
|
|||||||
using Expression = ExpressionClasses::Expression;
|
using Expression = ExpressionClasses::Expression;
|
||||||
using Id = ExpressionClasses::Id;
|
using Id = ExpressionClasses::Id;
|
||||||
|
|
||||||
using Builtins = dev::eth::EVMBuiltins<Pattern>;
|
using Builtins = evmasm::EVMBuiltins<Pattern>;
|
||||||
static constexpr size_t WordSize = 256;
|
static constexpr size_t WordSize = 256;
|
||||||
using Word = u256;
|
using Word = u256;
|
||||||
|
|
||||||
@ -159,4 +157,3 @@ struct ExpressionTemplate
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -16,6 +16,7 @@ set(sources
|
|||||||
SemVerHandler.cpp
|
SemVerHandler.cpp
|
||||||
SemVerHandler.h
|
SemVerHandler.h
|
||||||
SourceLocation.h
|
SourceLocation.h
|
||||||
|
SourceLocation.cpp
|
||||||
SourceReferenceExtractor.cpp
|
SourceReferenceExtractor.cpp
|
||||||
SourceReferenceExtractor.h
|
SourceReferenceExtractor.h
|
||||||
SourceReferenceFormatter.cpp
|
SourceReferenceFormatter.cpp
|
||||||
@ -28,4 +29,4 @@ set(sources
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(langutil ${sources})
|
add_library(langutil ${sources})
|
||||||
target_link_libraries(langutil PUBLIC devcore)
|
target_link_libraries(langutil PUBLIC solutil)
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
using namespace solidity;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
char CharStream::advanceAndGet(size_t _chars)
|
char CharStream::advanceAndGet(size_t _chars)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
inline bool isDecimalDigit(char c)
|
inline bool isDecimalDigit(char c)
|
||||||
|
@ -20,8 +20,9 @@
|
|||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
using namespace langutil;
|
using namespace solidity;
|
||||||
using namespace dev::eth;
|
using namespace solidity::evmasm;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
bool EVMVersion::hasOpcode(Instruction _opcode) const
|
bool EVMVersion::hasOpcode(Instruction _opcode) const
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +87,7 @@ public:
|
|||||||
bool hasChainID() const { return *this >= istanbul(); }
|
bool hasChainID() const { return *this >= istanbul(); }
|
||||||
bool hasSelfBalance() const { return *this >= istanbul(); }
|
bool hasSelfBalance() const { return *this >= istanbul(); }
|
||||||
|
|
||||||
bool hasOpcode(dev::eth::Instruction _opcode) const;
|
bool hasOpcode(evmasm::Instruction _opcode) const;
|
||||||
|
|
||||||
/// Whether we have to retain the costs for the call opcode itself (false),
|
/// Whether we have to retain the costs for the call opcode itself (false),
|
||||||
/// or whether we can just forward easily all remaining gas (true).
|
/// or whether we can just forward easily all remaining gas (true).
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
||||||
{
|
{
|
||||||
@ -67,7 +67,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
|
|||||||
auto err = make_shared<Error>(_type);
|
auto err = make_shared<Error>(_type);
|
||||||
*err <<
|
*err <<
|
||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
errinfo_comment(_description);
|
util::errinfo_comment(_description);
|
||||||
|
|
||||||
m_errorList.push_back(err);
|
m_errorList.push_back(err);
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se
|
|||||||
*err <<
|
*err <<
|
||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
errinfo_secondarySourceLocation(_secondaryLocation) <<
|
errinfo_secondarySourceLocation(_secondaryLocation) <<
|
||||||
errinfo_comment(_description);
|
util::errinfo_comment(_description);
|
||||||
|
|
||||||
m_errorList.push_back(err);
|
m_errorList.push_back(err);
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
|
|||||||
if (m_warningCount == c_maxWarningsAllowed)
|
if (m_warningCount == c_maxWarningsAllowed)
|
||||||
{
|
{
|
||||||
auto err = make_shared<Error>(Error::Type::Warning);
|
auto err = make_shared<Error>(Error::Type::Warning);
|
||||||
*err << errinfo_comment("There are more than 256 warnings. Ignoring the rest.");
|
*err << util::errinfo_comment("There are more than 256 warnings. Ignoring the rest.");
|
||||||
m_errorList.push_back(err);
|
m_errorList.push_back(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
|
|||||||
if (m_errorCount > c_maxErrorsAllowed)
|
if (m_errorCount > c_maxErrorsAllowed)
|
||||||
{
|
{
|
||||||
auto err = make_shared<Error>(Error::Type::Warning);
|
auto err = make_shared<Error>(Error::Type::Warning);
|
||||||
*err << errinfo_comment("There are more than 256 errors. Aborting.");
|
*err << util::errinfo_comment("There are more than 256 errors. Aborting.");
|
||||||
m_errorList.push_back(err);
|
m_errorList.push_back(err);
|
||||||
BOOST_THROW_EXCEPTION(FatalError());
|
BOOST_THROW_EXCEPTION(FatalError());
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libdevcore/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/filtered.hpp>
|
#include <boost/range/adaptor/filtered.hpp>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
class ErrorReporter
|
class ErrorReporter
|
||||||
@ -98,7 +98,7 @@ public:
|
|||||||
|
|
||||||
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
|
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
|
||||||
|
|
||||||
std::string errorStr = dev::joinHumanReadable(descs | filterEmpty, " ");
|
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
|
||||||
|
|
||||||
error(Error::Type::TypeError, _location, errorStr);
|
error(Error::Type::TypeError, _location, errorStr);
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
Error::Error(Type _type, SourceLocation const& _location, string const& _description):
|
Error::Error(Type _type, SourceLocation const& _location, string const& _description):
|
||||||
m_type(_type)
|
m_type(_type)
|
||||||
@ -54,7 +54,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
|
|||||||
if (!_location.isEmpty())
|
if (!_location.isEmpty())
|
||||||
*this << errinfo_sourceLocation(_location);
|
*this << errinfo_sourceLocation(_location);
|
||||||
if (!_description.empty())
|
if (!_description.empty())
|
||||||
*this << errinfo_comment(_description);
|
*this << util::errinfo_comment(_description);
|
||||||
}
|
}
|
||||||
|
|
||||||
Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location):
|
Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location):
|
||||||
@ -62,5 +62,5 @@ Error::Error(Error::Type _type, std::string const& _description, SourceLocation
|
|||||||
{
|
{
|
||||||
if (!_location.isEmpty())
|
if (!_location.isEmpty())
|
||||||
*this << errinfo_sourceLocation(_location);
|
*this << errinfo_sourceLocation(_location);
|
||||||
*this << errinfo_comment(_description);
|
*this << util::errinfo_comment(_description);
|
||||||
}
|
}
|
||||||
|
@ -26,32 +26,36 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <libdevcore/Exceptions.h>
|
#include <libsolutil/Exceptions.h>
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
class Error;
|
class Error;
|
||||||
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
||||||
|
|
||||||
struct CompilerError: virtual dev::Exception {};
|
struct CompilerError: virtual util::Exception {};
|
||||||
struct InternalCompilerError: virtual dev::Exception {};
|
struct InternalCompilerError: virtual util::Exception {};
|
||||||
struct FatalError: virtual dev::Exception {};
|
struct FatalError: virtual util::Exception {};
|
||||||
struct UnimplementedFeatureError: virtual dev::Exception {};
|
struct UnimplementedFeatureError: virtual util::Exception {};
|
||||||
|
struct InvalidAstError: virtual util::Exception {};
|
||||||
|
|
||||||
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
|
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
|
||||||
#define solAssert(CONDITION, DESCRIPTION) \
|
#define solAssert(CONDITION, DESCRIPTION) \
|
||||||
assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION)
|
assertThrow(CONDITION, ::solidity::langutil::InternalCompilerError, DESCRIPTION)
|
||||||
|
|
||||||
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
|
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
|
||||||
assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION)
|
assertThrow(CONDITION, ::solidity::langutil::UnimplementedFeatureError, DESCRIPTION)
|
||||||
|
|
||||||
#define solUnimplemented(DESCRIPTION) \
|
#define solUnimplemented(DESCRIPTION) \
|
||||||
solUnimplementedAssert(false, DESCRIPTION)
|
solUnimplementedAssert(false, DESCRIPTION)
|
||||||
|
|
||||||
class Error: virtual public dev::Exception
|
#define astAssert(CONDITION, DESCRIPTION) \
|
||||||
|
assertThrow(CONDITION, ::solidity::langutil::InvalidAstError, DESCRIPTION)
|
||||||
|
|
||||||
|
class Error: virtual public util::Exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Type
|
enum class Type
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
using namespace solidity;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
int ParserBase::position() const
|
int ParserBase::position() const
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
|
@ -60,9 +60,10 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace langutil;
|
|
||||||
|
|
||||||
string langutil::to_string(ScannerError _errorCode)
|
namespace solidity::langutil {
|
||||||
|
|
||||||
|
string to_string(ScannerError _errorCode)
|
||||||
{
|
{
|
||||||
switch (_errorCode)
|
switch (_errorCode)
|
||||||
{
|
{
|
||||||
@ -84,14 +85,11 @@ string langutil::to_string(ScannerError _errorCode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ostream& langutil::operator<<(ostream& os, ScannerError _errorCode)
|
ostream& operator<<(ostream& os, ScannerError _errorCode)
|
||||||
{
|
{
|
||||||
return os << to_string(_errorCode);
|
return os << to_string(_errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace langutil
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Scoped helper for literal recording. Automatically drops the literal
|
/// Scoped helper for literal recording. Automatically drops the literal
|
||||||
/// if aborting the scanning before it's complete.
|
/// if aborting the scanning before it's complete.
|
||||||
enum LiteralType
|
enum LiteralType
|
||||||
@ -110,18 +108,18 @@ public:
|
|||||||
m_complete(false)
|
m_complete(false)
|
||||||
{
|
{
|
||||||
if (_type == LITERAL_TYPE_COMMENT)
|
if (_type == LITERAL_TYPE_COMMENT)
|
||||||
m_scanner->m_nextSkippedComment.literal.clear();
|
m_scanner->m_skippedComments[Scanner::NextNext].literal.clear();
|
||||||
else
|
else
|
||||||
m_scanner->m_nextToken.literal.clear();
|
m_scanner->m_tokens[Scanner::NextNext].literal.clear();
|
||||||
}
|
}
|
||||||
~LiteralScope()
|
~LiteralScope()
|
||||||
{
|
{
|
||||||
if (!m_complete)
|
if (!m_complete)
|
||||||
{
|
{
|
||||||
if (m_type == LITERAL_TYPE_COMMENT)
|
if (m_type == LITERAL_TYPE_COMMENT)
|
||||||
m_scanner->m_nextSkippedComment.literal.clear();
|
m_scanner->m_skippedComments[Scanner::NextNext].literal.clear();
|
||||||
else
|
else
|
||||||
m_scanner->m_nextToken.literal.clear();
|
m_scanner->m_tokens[Scanner::NextNext].literal.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void complete() { m_complete = true; }
|
void complete() { m_complete = true; }
|
||||||
@ -132,8 +130,6 @@ private:
|
|||||||
bool m_complete;
|
bool m_complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scanner::reset(CharStream _source)
|
void Scanner::reset(CharStream _source)
|
||||||
{
|
{
|
||||||
m_source = make_shared<CharStream>(std::move(_source));
|
m_source = make_shared<CharStream>(std::move(_source));
|
||||||
@ -155,6 +151,7 @@ void Scanner::reset()
|
|||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
next();
|
next();
|
||||||
next();
|
next();
|
||||||
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scanner::setPosition(size_t _offset)
|
void Scanner::setPosition(size_t _offset)
|
||||||
@ -162,6 +159,7 @@ void Scanner::setPosition(size_t _offset)
|
|||||||
m_char = m_source->setPosition(_offset);
|
m_char = m_source->setPosition(_offset);
|
||||||
scanToken();
|
scanToken();
|
||||||
next();
|
next();
|
||||||
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scanner::supportPeriodInIdentifier(bool _value)
|
void Scanner::supportPeriodInIdentifier(bool _value)
|
||||||
@ -226,13 +224,14 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint)
|
|||||||
void Scanner::rescan()
|
void Scanner::rescan()
|
||||||
{
|
{
|
||||||
size_t rollbackTo = 0;
|
size_t rollbackTo = 0;
|
||||||
if (m_skippedComment.literal.empty())
|
if (m_skippedComments[Current].literal.empty())
|
||||||
rollbackTo = m_currentToken.location.start;
|
rollbackTo = m_tokens[Current].location.start;
|
||||||
else
|
else
|
||||||
rollbackTo = m_skippedComment.location.start;
|
rollbackTo = m_skippedComments[Current].location.start;
|
||||||
m_char = m_source->rollback(size_t(m_source->position()) - rollbackTo);
|
m_char = m_source->rollback(size_t(m_source->position()) - rollbackTo);
|
||||||
next();
|
next();
|
||||||
next();
|
next();
|
||||||
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that tokens can be stored in a byte.
|
// Ensure that tokens can be stored in a byte.
|
||||||
@ -240,11 +239,14 @@ BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100);
|
|||||||
|
|
||||||
Token Scanner::next()
|
Token Scanner::next()
|
||||||
{
|
{
|
||||||
m_currentToken = m_nextToken;
|
m_tokens[Current] = std::move(m_tokens[Next]);
|
||||||
m_skippedComment = m_nextSkippedComment;
|
m_tokens[Next] = std::move(m_tokens[NextNext]);
|
||||||
|
m_skippedComments[Current] = std::move(m_skippedComments[Next]);
|
||||||
|
m_skippedComments[Next] = std::move(m_skippedComments[NextNext]);
|
||||||
|
|
||||||
scanToken();
|
scanToken();
|
||||||
|
|
||||||
return m_currentToken.token;
|
return m_tokens[Current].token;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Scanner::selectToken(char _next, Token _then, Token _else)
|
Token Scanner::selectToken(char _next, Token _then, Token _else)
|
||||||
@ -425,10 +427,10 @@ Token Scanner::scanSlash()
|
|||||||
{
|
{
|
||||||
// doxygen style /// comment
|
// doxygen style /// comment
|
||||||
Token comment;
|
Token comment;
|
||||||
m_nextSkippedComment.location.start = firstSlashPosition;
|
m_skippedComments[NextNext].location.start = firstSlashPosition;
|
||||||
comment = scanSingleLineDocComment();
|
comment = scanSingleLineDocComment();
|
||||||
m_nextSkippedComment.location.end = sourcePos();
|
m_skippedComments[NextNext].location.end = sourcePos();
|
||||||
m_nextSkippedComment.token = comment;
|
m_skippedComments[NextNext].token = comment;
|
||||||
return Token::Whitespace;
|
return Token::Whitespace;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -451,10 +453,10 @@ Token Scanner::scanSlash()
|
|||||||
}
|
}
|
||||||
// we actually have a multiline documentation comment
|
// we actually have a multiline documentation comment
|
||||||
Token comment;
|
Token comment;
|
||||||
m_nextSkippedComment.location.start = firstSlashPosition;
|
m_skippedComments[NextNext].location.start = firstSlashPosition;
|
||||||
comment = scanMultiLineDocComment();
|
comment = scanMultiLineDocComment();
|
||||||
m_nextSkippedComment.location.end = sourcePos();
|
m_skippedComments[NextNext].location.end = sourcePos();
|
||||||
m_nextSkippedComment.token = comment;
|
m_skippedComments[NextNext].token = comment;
|
||||||
if (comment == Token::Illegal)
|
if (comment == Token::Illegal)
|
||||||
return Token::Illegal; // error already set
|
return Token::Illegal; // error already set
|
||||||
else
|
else
|
||||||
@ -471,11 +473,8 @@ Token Scanner::scanSlash()
|
|||||||
|
|
||||||
void Scanner::scanToken()
|
void Scanner::scanToken()
|
||||||
{
|
{
|
||||||
m_nextToken.error = ScannerError::NoError;
|
m_tokens[NextNext] = {};
|
||||||
m_nextToken.literal.clear();
|
m_skippedComments[NextNext] = {};
|
||||||
m_nextToken.extendedTokenInfo = make_tuple(0, 0);
|
|
||||||
m_nextSkippedComment.literal.clear();
|
|
||||||
m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0);
|
|
||||||
|
|
||||||
Token token;
|
Token token;
|
||||||
// M and N are for the purposes of grabbing different type sizes
|
// M and N are for the purposes of grabbing different type sizes
|
||||||
@ -484,7 +483,7 @@ void Scanner::scanToken()
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Remember the position of the next token
|
// Remember the position of the next token
|
||||||
m_nextToken.location.start = sourcePos();
|
m_tokens[NextNext].location.start = sourcePos();
|
||||||
switch (m_char)
|
switch (m_char)
|
||||||
{
|
{
|
||||||
case '"':
|
case '"':
|
||||||
@ -679,9 +678,9 @@ void Scanner::scanToken()
|
|||||||
// whitespace.
|
// whitespace.
|
||||||
}
|
}
|
||||||
while (token == Token::Whitespace);
|
while (token == Token::Whitespace);
|
||||||
m_nextToken.location.end = sourcePos();
|
m_tokens[NextNext].location.end = sourcePos();
|
||||||
m_nextToken.token = token;
|
m_tokens[NextNext].token = token;
|
||||||
m_nextToken.extendedTokenInfo = make_tuple(m, n);
|
m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Scanner::scanEscape()
|
bool Scanner::scanEscape()
|
||||||
@ -931,5 +930,7 @@ tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
|
|||||||
while (isIdentifierPart(m_char) || (m_char == '.' && m_supportPeriodInIdentifier))
|
while (isIdentifierPart(m_char) || (m_char == '.' && m_supportPeriodInIdentifier))
|
||||||
addLiteralCharAndAdvance();
|
addLiteralCharAndAdvance();
|
||||||
literal.complete();
|
literal.complete();
|
||||||
return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal);
|
return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace solidity::langutil
|
||||||
|
@ -55,13 +55,13 @@
|
|||||||
#include <liblangutil/Token.h>
|
#include <liblangutil/Token.h>
|
||||||
#include <liblangutil/CharStream.h>
|
#include <liblangutil/CharStream.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
class AstRawString;
|
class AstRawString;
|
||||||
@ -121,32 +121,32 @@ public:
|
|||||||
/// @returns the current token
|
/// @returns the current token
|
||||||
Token currentToken() const
|
Token currentToken() const
|
||||||
{
|
{
|
||||||
return m_currentToken.token;
|
return m_tokens[Current].token;
|
||||||
}
|
}
|
||||||
ElementaryTypeNameToken currentElementaryTypeNameToken() const
|
ElementaryTypeNameToken currentElementaryTypeNameToken() const
|
||||||
{
|
{
|
||||||
unsigned firstSize;
|
unsigned firstSize;
|
||||||
unsigned secondSize;
|
unsigned secondSize;
|
||||||
std::tie(firstSize, secondSize) = m_currentToken.extendedTokenInfo;
|
std::tie(firstSize, secondSize) = m_tokens[Current].extendedTokenInfo;
|
||||||
return ElementaryTypeNameToken(m_currentToken.token, firstSize, secondSize);
|
return ElementaryTypeNameToken(m_tokens[Current].token, firstSize, secondSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceLocation currentLocation() const { return m_currentToken.location; }
|
SourceLocation currentLocation() const { return m_tokens[Current].location; }
|
||||||
std::string const& currentLiteral() const { return m_currentToken.literal; }
|
std::string const& currentLiteral() const { return m_tokens[Current].literal; }
|
||||||
std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_currentToken.extendedTokenInfo; }
|
std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_tokens[Current].extendedTokenInfo; }
|
||||||
|
|
||||||
/// Retrieves the last error that occurred during lexical analysis.
|
/// Retrieves the last error that occurred during lexical analysis.
|
||||||
/// @note If no error occurred, the value is undefined.
|
/// @note If no error occurred, the value is undefined.
|
||||||
ScannerError currentError() const noexcept { return m_currentToken.error; }
|
ScannerError currentError() const noexcept { return m_tokens[Current].error; }
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
///@{
|
///@{
|
||||||
///@name Information about the current comment token
|
///@name Information about the current comment token
|
||||||
|
|
||||||
SourceLocation currentCommentLocation() const { return m_skippedComment.location; }
|
SourceLocation currentCommentLocation() const { return m_skippedComments[Current].location; }
|
||||||
std::string const& currentCommentLiteral() const { return m_skippedComment.literal; }
|
std::string const& currentCommentLiteral() const { return m_skippedComments[Current].literal; }
|
||||||
/// Called by the parser during FunctionDefinition parsing to clear the current comment
|
/// Called by the parser during FunctionDefinition parsing to clear the current comment
|
||||||
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
|
void clearCurrentCommentLiteral() { m_skippedComments[Current].literal.clear(); }
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
@ -154,9 +154,11 @@ public:
|
|||||||
///@name Information about the next token
|
///@name Information about the next token
|
||||||
|
|
||||||
/// @returns the next token without advancing input.
|
/// @returns the next token without advancing input.
|
||||||
Token peekNextToken() const { return m_nextToken.token; }
|
Token peekNextToken() const { return m_tokens[Next].token; }
|
||||||
SourceLocation peekLocation() const { return m_nextToken.location; }
|
SourceLocation peekLocation() const { return m_tokens[Next].location; }
|
||||||
std::string const& peekLiteral() const { return m_nextToken.literal; }
|
std::string const& peekLiteral() const { return m_tokens[Next].literal; }
|
||||||
|
|
||||||
|
Token peekNextNextToken() const { return m_tokens[NextNext].token; }
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
///@{
|
///@{
|
||||||
@ -176,7 +178,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
inline Token setError(ScannerError _error) noexcept
|
inline Token setError(ScannerError _error) noexcept
|
||||||
{
|
{
|
||||||
m_nextToken.error = _error;
|
m_tokens[NextNext].error = _error;
|
||||||
return Token::Illegal;
|
return Token::Illegal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,8 +194,8 @@ private:
|
|||||||
|
|
||||||
///@{
|
///@{
|
||||||
///@name Literal buffer support
|
///@name Literal buffer support
|
||||||
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
|
inline void addLiteralChar(char c) { m_tokens[NextNext].literal.push_back(c); }
|
||||||
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
|
inline void addCommentLiteralChar(char c) { m_skippedComments[NextNext].literal.push_back(c); }
|
||||||
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
|
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
|
||||||
void addUnicodeAsUTF8(unsigned codepoint);
|
void addUnicodeAsUTF8(unsigned codepoint);
|
||||||
///@}
|
///@}
|
||||||
@ -252,11 +254,10 @@ private:
|
|||||||
|
|
||||||
bool m_supportPeriodInIdentifier = false;
|
bool m_supportPeriodInIdentifier = false;
|
||||||
|
|
||||||
TokenDesc m_skippedComment; // desc for current skipped comment
|
enum TokenIndex { Current, Next, NextNext };
|
||||||
TokenDesc m_nextSkippedComment; // desc for next skipped comment
|
|
||||||
|
|
||||||
TokenDesc m_currentToken; // desc for current token (as returned by Next())
|
TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment
|
||||||
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
|
TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token
|
||||||
|
|
||||||
std::shared_ptr<CharStream> m_source;
|
std::shared_ptr<CharStream> m_source;
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
SemVerVersion::SemVerVersion(string const& _versionString)
|
SemVerVersion::SemVerVersion(string const& _versionString)
|
||||||
{
|
{
|
||||||
|
@ -26,10 +26,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
class SemVerError: dev::Exception
|
class SemVerError: util::Exception
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
51
liblangutil/SourceLocation.cpp
Normal file
51
liblangutil/SourceLocation.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
using namespace solidity;
|
||||||
|
namespace solidity::langutil
|
||||||
|
{
|
||||||
|
|
||||||
|
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex)
|
||||||
|
{
|
||||||
|
// Expected input: "start:length:sourceindex"
|
||||||
|
enum SrcElem : size_t { Start, Length, Index };
|
||||||
|
|
||||||
|
std::vector<std::string> pos;
|
||||||
|
|
||||||
|
boost::algorithm::split(pos, _input, boost::is_any_of(":"));
|
||||||
|
|
||||||
|
astAssert(
|
||||||
|
pos.size() == 3 &&
|
||||||
|
_maxIndex >= static_cast<size_t>(stoi(pos[Index])),
|
||||||
|
"'src'-field ill-formatted or src-index too high"
|
||||||
|
);
|
||||||
|
|
||||||
|
int start = stoi(pos[Start]);
|
||||||
|
int end = start + stoi(pos[Length]);
|
||||||
|
|
||||||
|
// ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty
|
||||||
|
std::shared_ptr<langutil::CharStream> source = std::make_shared<langutil::CharStream>("", _sourceName);
|
||||||
|
|
||||||
|
return SourceLocation{start, end, source};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,18 +22,17 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
#include <libdevcore/Common.h> // defines noexcept macro for MSVC
|
#include <libsolutil/Exceptions.h>
|
||||||
#include <libdevcore/Exceptions.h>
|
|
||||||
#include <liblangutil/CharStream.h>
|
#include <liblangutil/CharStream.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
struct SourceLocationError: virtual dev::Exception {};
|
struct SourceLocationError: virtual util::Exception {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of an interval of source positions.
|
* Representation of an interval of source positions.
|
||||||
@ -46,9 +45,28 @@ struct SourceLocation
|
|||||||
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
|
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
|
||||||
}
|
}
|
||||||
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
|
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
|
||||||
inline bool operator<(SourceLocation const& _other) const;
|
|
||||||
inline bool contains(SourceLocation const& _other) const;
|
inline bool operator<(SourceLocation const& _other) const
|
||||||
inline bool intersects(SourceLocation const& _other) const;
|
{
|
||||||
|
if (!source|| !_other.source)
|
||||||
|
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
|
||||||
|
else
|
||||||
|
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool contains(SourceLocation const& _other) const
|
||||||
|
{
|
||||||
|
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
|
||||||
|
return false;
|
||||||
|
return start <= _other.start && _other.end <= end;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool intersects(SourceLocation const& _other) const
|
||||||
|
{
|
||||||
|
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
|
||||||
|
return false;
|
||||||
|
return _other.start < end && start < _other.end;
|
||||||
|
}
|
||||||
|
|
||||||
bool isEmpty() const { return start == -1 && end == -1; }
|
bool isEmpty() const { return start == -1 && end == -1; }
|
||||||
|
|
||||||
@ -86,6 +104,8 @@ struct SourceLocation
|
|||||||
std::shared_ptr<CharStream> source;
|
std::shared_ptr<CharStream> source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex = -1);
|
||||||
|
|
||||||
/// Stream output for Location (used e.g. in boost exceptions).
|
/// Stream output for Location (used e.g. in boost exceptions).
|
||||||
inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location)
|
inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
@ -100,26 +120,4 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
|
|||||||
return _out;
|
return _out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SourceLocation::operator<(SourceLocation const& _other) const
|
|
||||||
{
|
|
||||||
if (!source|| !_other.source)
|
|
||||||
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
|
|
||||||
else
|
|
||||||
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SourceLocation::contains(SourceLocation const& _other) const
|
|
||||||
{
|
|
||||||
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
|
|
||||||
return false;
|
|
||||||
return start <= _other.start && _other.end <= end;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SourceLocation::intersects(SourceLocation const& _other) const
|
|
||||||
{
|
|
||||||
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
|
|
||||||
return false;
|
|
||||||
return _other.start < end && start < _other.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,14 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
|
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(util::Exception const& _exception, string _category)
|
||||||
{
|
{
|
||||||
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
|
||||||
|
|
||||||
string const* message = boost::get_error_info<errinfo_comment>(_exception);
|
string const* message = boost::get_error_info<util::errinfo_comment>(_exception);
|
||||||
SourceReference primary = extract(location, message ? *message : "");
|
SourceReference primary = extract(location, message ? *message : "");
|
||||||
|
|
||||||
std::vector<SourceReference> secondary;
|
std::vector<SourceReference> secondary;
|
||||||
@ -46,6 +46,9 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio
|
|||||||
if (!_location || !_location->source.get()) // Nothing we can extract here
|
if (!_location || !_location->source.get()) // Nothing we can extract here
|
||||||
return SourceReference::MessageOnly(std::move(message));
|
return SourceReference::MessageOnly(std::move(message));
|
||||||
|
|
||||||
|
if (_location->source->source().empty()) // No source text, so we can only extract the source name
|
||||||
|
return SourceReference::MessageOnly(std::move(message), _location->source->name());
|
||||||
|
|
||||||
shared_ptr<CharStream> const& source = _location->source;
|
shared_ptr<CharStream> const& source = _location->source;
|
||||||
|
|
||||||
LineColumn const interest = source->translatePositionToLineColumn(_location->start);
|
LineColumn const interest = source->translatePositionToLineColumn(_location->start);
|
||||||
|
@ -21,12 +21,12 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::util
|
||||||
{
|
{
|
||||||
struct Exception;
|
struct Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
struct LineColumn
|
struct LineColumn
|
||||||
@ -49,10 +49,11 @@ struct SourceReference
|
|||||||
int endColumn = {-1}; ///< Highlighting range-end of text field.
|
int endColumn = {-1}; ///< Highlighting range-end of text field.
|
||||||
|
|
||||||
/// Constructs a SourceReference containing a message only.
|
/// Constructs a SourceReference containing a message only.
|
||||||
static SourceReference MessageOnly(std::string _msg)
|
static SourceReference MessageOnly(std::string _msg, std::string _sourceName = {})
|
||||||
{
|
{
|
||||||
SourceReference sref;
|
SourceReference sref;
|
||||||
sref.message = std::move(_msg);
|
sref.message = std::move(_msg);
|
||||||
|
sref.sourceName = std::move(_sourceName);
|
||||||
return sref;
|
return sref;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -68,7 +69,7 @@ namespace SourceReferenceExtractor
|
|||||||
std::vector<SourceReference> secondary;
|
std::vector<SourceReference> secondary;
|
||||||
};
|
};
|
||||||
|
|
||||||
Message extract(dev::Exception const& _exception, std::string _category);
|
Message extract(util::Exception const& _exception, std::string _category);
|
||||||
SourceReference extract(SourceLocation const* _location, std::string message = "");
|
SourceReference extract(SourceLocation const* _location, std::string message = "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,9 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace langutil;
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
|
||||||
{
|
{
|
||||||
@ -70,7 +71,7 @@ void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
|
|||||||
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
|
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceReferenceFormatter::printExceptionInformation(dev::Exception const& _exception, std::string const& _category)
|
void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category)
|
||||||
{
|
{
|
||||||
printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category));
|
printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category));
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <liblangutil/SourceReferenceExtractor.h>
|
#include <liblangutil/SourceReferenceExtractor.h>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::util
|
||||||
{
|
{
|
||||||
struct Exception; // forward
|
struct Exception; // forward
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
class Scanner;
|
class Scanner;
|
||||||
@ -52,7 +52,7 @@ public:
|
|||||||
virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
|
virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
|
||||||
|
|
||||||
virtual void printSourceLocation(SourceLocation const* _location);
|
virtual void printSourceLocation(SourceLocation const* _location);
|
||||||
virtual void printExceptionInformation(dev::Exception const& _exception, std::string const& _category);
|
virtual void printExceptionInformation(util::Exception const& _exception, std::string const& _category);
|
||||||
virtual void printErrorInformation(Error const& _error);
|
virtual void printErrorInformation(Error const& _error);
|
||||||
|
|
||||||
static std::string formatErrorInformation(Error const& _error)
|
static std::string formatErrorInformation(Error const& _error)
|
||||||
@ -64,7 +64,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string formatExceptionInformation(
|
static std::string formatExceptionInformation(
|
||||||
dev::Exception const& _exception,
|
util::Exception const& _exception,
|
||||||
std::string const& _name
|
std::string const& _name
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -25,9 +25,10 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace solidity;
|
||||||
using namespace dev::formatting;
|
using namespace solidity::langutil;
|
||||||
using namespace langutil;
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::util::formatting;
|
||||||
|
|
||||||
AnsiColorized SourceReferenceFormatterHuman::normalColored() const
|
AnsiColorized SourceReferenceFormatterHuman::normalColored() const
|
||||||
{
|
{
|
||||||
|
@ -23,18 +23,18 @@
|
|||||||
#include <liblangutil/SourceReferenceExtractor.h>
|
#include <liblangutil/SourceReferenceExtractor.h>
|
||||||
#include <liblangutil/SourceReferenceFormatter.h> // SourceReferenceFormatterBase
|
#include <liblangutil/SourceReferenceFormatter.h> // SourceReferenceFormatterBase
|
||||||
|
|
||||||
#include <libdevcore/AnsiColorized.h>
|
#include <libsolutil/AnsiColorized.h>
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace dev
|
namespace solidity::util
|
||||||
{
|
{
|
||||||
struct Exception; // forward
|
struct Exception; // forward
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
@ -52,7 +52,7 @@ public:
|
|||||||
using SourceReferenceFormatter::printExceptionInformation;
|
using SourceReferenceFormatter::printExceptionInformation;
|
||||||
|
|
||||||
static std::string formatExceptionInformation(
|
static std::string formatExceptionInformation(
|
||||||
dev::Exception const& _exception,
|
util::Exception const& _exception,
|
||||||
std::string const& _name,
|
std::string const& _name,
|
||||||
bool colored = false
|
bool colored = false
|
||||||
)
|
)
|
||||||
@ -65,13 +65,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
dev::AnsiColorized normalColored() const;
|
util::AnsiColorized normalColored() const;
|
||||||
dev::AnsiColorized frameColored() const;
|
util::AnsiColorized frameColored() const;
|
||||||
dev::AnsiColorized errorColored() const;
|
util::AnsiColorized errorColored() const;
|
||||||
dev::AnsiColorized messageColored() const;
|
util::AnsiColorized messageColored() const;
|
||||||
dev::AnsiColorized secondaryColored() const;
|
util::AnsiColorized secondaryColored() const;
|
||||||
dev::AnsiColorized highlightColored() const;
|
util::AnsiColorized highlightColored() const;
|
||||||
dev::AnsiColorized diagColored() const;
|
util::AnsiColorized diagColored() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_colored;
|
bool m_colored;
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second)
|
void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second)
|
||||||
@ -82,7 +82,6 @@ void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _fi
|
|||||||
|
|
||||||
namespace TokenTraits
|
namespace TokenTraits
|
||||||
{
|
{
|
||||||
|
|
||||||
char const* toString(Token tok)
|
char const* toString(Token tok)
|
||||||
{
|
{
|
||||||
switch (tok)
|
switch (tok)
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
#include <liblangutil/UndefMacros.h>
|
#include <liblangutil/UndefMacros.h>
|
||||||
|
|
||||||
@ -50,7 +50,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
|
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
set(sources
|
|
||||||
CodeFragment.cpp
|
|
||||||
CodeFragment.h
|
|
||||||
Compiler.cpp
|
|
||||||
Compiler.h
|
|
||||||
CompilerState.cpp
|
|
||||||
CompilerState.h
|
|
||||||
Exceptions.h
|
|
||||||
Parser.cpp
|
|
||||||
Parser.h
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(lll ${sources})
|
|
||||||
target_link_libraries(lll PUBLIC evmasm devcore)
|
|
@ -1,757 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file CodeFragment.cpp
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <liblll/CodeFragment.h>
|
|
||||||
#include <liblll/CompilerState.h>
|
|
||||||
#include <liblll/Parser.h>
|
|
||||||
#include <libevmasm/Instruction.h>
|
|
||||||
#include <libdevcore/CommonIO.h>
|
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
#endif // defined(__GNUC__)
|
|
||||||
|
|
||||||
#include <boost/spirit/include/support_utree.hpp>
|
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif // defined(__GNUC__)
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace dev;
|
|
||||||
using namespace dev::eth;
|
|
||||||
using namespace dev::lll;
|
|
||||||
|
|
||||||
void CodeFragment::finalise(CompilerState const& _cs)
|
|
||||||
{
|
|
||||||
// NOTE: add this as a safeguard in case the user didn't issue an
|
|
||||||
// explicit stop at the end of the sequence
|
|
||||||
m_asm.append(Instruction::STOP);
|
|
||||||
|
|
||||||
if (_cs.usedAlloc && _cs.vars.size() && !m_finalised)
|
|
||||||
{
|
|
||||||
m_finalised = true;
|
|
||||||
m_asm.injectStart(Instruction::MSTORE8);
|
|
||||||
m_asm.injectStart((u256)((_cs.vars.size() + 2) * 32) - 1);
|
|
||||||
m_asm.injectStart((u256)1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
/// Returns true iff the instruction is valid in "inline assembly".
|
|
||||||
bool validAssemblyInstruction(string us)
|
|
||||||
{
|
|
||||||
auto it = c_instructions.find(us);
|
|
||||||
return !(
|
|
||||||
it == c_instructions.end() ||
|
|
||||||
isPushInstruction(it->second)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true iff the instruction is valid as a function.
|
|
||||||
bool validFunctionalInstruction(string us)
|
|
||||||
{
|
|
||||||
auto it = c_instructions.find(us);
|
|
||||||
return !(
|
|
||||||
it == c_instructions.end() ||
|
|
||||||
isPushInstruction(it->second) ||
|
|
||||||
isDupInstruction(it->second) ||
|
|
||||||
isSwapInstruction(it->second) ||
|
|
||||||
it->second == Instruction::JUMPDEST
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM):
|
|
||||||
m_readFile(_readFile)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
std::cout << "CodeFragment. Locals:";
|
|
||||||
for (auto const& i: _s.defs)
|
|
||||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
|
||||||
std::cout << "Args:";
|
|
||||||
for (auto const& i: _s.args)
|
|
||||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
|
||||||
std::cout << "Outers:";
|
|
||||||
for (auto const& i: _s.outers)
|
|
||||||
std::cout << i.first << ":" << i.second.m_asm.out();
|
|
||||||
debugOutAST(std::cout, _t);
|
|
||||||
std::cout << endl << flush;
|
|
||||||
*/
|
|
||||||
switch (_t.which())
|
|
||||||
{
|
|
||||||
case sp::utree_type::list_type:
|
|
||||||
constructOperation(_t, _s);
|
|
||||||
break;
|
|
||||||
case sp::utree_type::string_type:
|
|
||||||
{
|
|
||||||
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
|
||||||
string s(sr.begin(), sr.end());
|
|
||||||
m_asm.append(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case sp::utree_type::symbol_type:
|
|
||||||
{
|
|
||||||
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
|
||||||
string s(sr.begin(), sr.end());
|
|
||||||
string us = boost::algorithm::to_upper_copy(s);
|
|
||||||
if (_allowASM && c_instructions.count(us) && validAssemblyInstruction(us))
|
|
||||||
m_asm.append(c_instructions.at(us));
|
|
||||||
else if (_s.defs.count(s))
|
|
||||||
m_asm.append(_s.defs.at(s).m_asm);
|
|
||||||
else if (_s.args.count(s))
|
|
||||||
m_asm.append(_s.args.at(s).m_asm);
|
|
||||||
else if (_s.outers.count(s))
|
|
||||||
m_asm.append(_s.outers.at(s).m_asm);
|
|
||||||
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
|
|
||||||
{
|
|
||||||
auto it = _s.vars.find(s);
|
|
||||||
if (it == _s.vars.end())
|
|
||||||
error<InvalidName>(std::string("Symbol not found: ") + s);
|
|
||||||
m_asm.append((u256)it->second.first);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error<BareSymbol>(s);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case sp::utree_type::any_type:
|
|
||||||
{
|
|
||||||
bigint i = *_t.get<bigint*>();
|
|
||||||
if (i < 0 || i > bigint(u256(0) - 1))
|
|
||||||
error<IntegerOutOfRange>(toString(i));
|
|
||||||
m_asm.append((u256)i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
error<CompilerException>("Unexpected fragment type");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|
||||||
{
|
|
||||||
if (_t.tag() == 0 && _t.empty())
|
|
||||||
error<EmptyList>();
|
|
||||||
else if (_t.tag() == 0 && _t.front().which() != sp::utree_type::symbol_type)
|
|
||||||
error<DataNotExecutable>();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string s;
|
|
||||||
string us;
|
|
||||||
switch (_t.tag())
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
auto sr = _t.front().get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
|
||||||
s = string(sr.begin(), sr.end());
|
|
||||||
us = boost::algorithm::to_upper_copy(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
us = "MLOAD";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
us = "SLOAD";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
us = "MSTORE";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
us = "SSTORE";
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
us = "SEQ";
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
us = "CALLDATALOAD";
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto firstAsString = [&]()
|
|
||||||
{
|
|
||||||
auto i = *++_t.begin();
|
|
||||||
if (i.tag())
|
|
||||||
error<InvalidName>(toString(i));
|
|
||||||
if (i.which() == sp::utree_type::string_type)
|
|
||||||
{
|
|
||||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
|
||||||
return string(sr.begin(), sr.end());
|
|
||||||
}
|
|
||||||
else if (i.which() == sp::utree_type::symbol_type)
|
|
||||||
{
|
|
||||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
|
||||||
return _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
|
|
||||||
}
|
|
||||||
return string();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto varAddress = [&](string const& n, bool createMissing = false)
|
|
||||||
{
|
|
||||||
if (n.empty())
|
|
||||||
error<InvalidName>("Empty variable name not allowed");
|
|
||||||
auto it = _s.vars.find(n);
|
|
||||||
if (it == _s.vars.end())
|
|
||||||
{
|
|
||||||
if (createMissing)
|
|
||||||
{
|
|
||||||
// Create new variable
|
|
||||||
bool ok;
|
|
||||||
tie(it, ok) = _s.vars.insert(make_pair(n, make_pair(_s.stackSize, 32)));
|
|
||||||
_s.stackSize += 32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error<InvalidName>(std::string("Symbol not found: ") + n);
|
|
||||||
}
|
|
||||||
return it->second.first;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Operations who args are not standard stack-pushers.
|
|
||||||
bool nonStandard = true;
|
|
||||||
if (us == "ASM")
|
|
||||||
{
|
|
||||||
int c = 0;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
if (c++)
|
|
||||||
{
|
|
||||||
auto fragment = CodeFragment(i, _s, m_readFile, true).m_asm;
|
|
||||||
if ((m_asm.deposit() + fragment.deposit()) < 0)
|
|
||||||
error<IncorrectParameterCount>("The assembly instruction resulted in stack underflow");
|
|
||||||
m_asm.append(fragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (us == "INCLUDE")
|
|
||||||
{
|
|
||||||
if (_t.size() != 2)
|
|
||||||
error<IncorrectParameterCount>(us);
|
|
||||||
string fileName = firstAsString();
|
|
||||||
if (fileName.empty())
|
|
||||||
error<InvalidName>("Empty file name provided");
|
|
||||||
if (!m_readFile)
|
|
||||||
error<InvalidName>("Import callback not present");
|
|
||||||
string contents = m_readFile(fileName);
|
|
||||||
if (contents.empty())
|
|
||||||
error<InvalidName>(std::string("File not found (or empty): ") + fileName);
|
|
||||||
m_asm.append(CodeFragment::compile(std::move(contents), _s, m_readFile).m_asm);
|
|
||||||
}
|
|
||||||
else if (us == "SET")
|
|
||||||
{
|
|
||||||
// TODO: move this to be a stack variable (and not a memory variable)
|
|
||||||
if (_t.size() != 3)
|
|
||||||
error<IncorrectParameterCount>(us);
|
|
||||||
int c = 0;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
if (c++ == 2)
|
|
||||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
|
||||||
m_asm.append((u256)varAddress(firstAsString(), true));
|
|
||||||
m_asm.append(Instruction::MSTORE);
|
|
||||||
}
|
|
||||||
else if (us == "UNSET")
|
|
||||||
{
|
|
||||||
// TODO: this doesn't actually free up anything, since it is a memory variable (see "SET")
|
|
||||||
if (_t.size() != 2)
|
|
||||||
error<IncorrectParameterCount>();
|
|
||||||
auto it = _s.vars.find(firstAsString());
|
|
||||||
if (it != _s.vars.end())
|
|
||||||
_s.vars.erase(it);
|
|
||||||
}
|
|
||||||
else if (us == "GET")
|
|
||||||
{
|
|
||||||
if (_t.size() != 2)
|
|
||||||
error<IncorrectParameterCount>(us);
|
|
||||||
m_asm.append((u256)varAddress(firstAsString()));
|
|
||||||
m_asm.append(Instruction::MLOAD);
|
|
||||||
}
|
|
||||||
else if (us == "WITH")
|
|
||||||
{
|
|
||||||
if (_t.size() != 4)
|
|
||||||
error<IncorrectParameterCount>();
|
|
||||||
string key = firstAsString();
|
|
||||||
if (_s.vars.find(key) != _s.vars.end())
|
|
||||||
error<InvalidName>(string("Symbol already used: ") + key);
|
|
||||||
|
|
||||||
// Create variable
|
|
||||||
// TODO: move this to be a stack variable (and not a memory variable)
|
|
||||||
size_t c = 0;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
if (c++ == 2)
|
|
||||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
|
||||||
m_asm.append((u256)varAddress(key, true));
|
|
||||||
m_asm.append(Instruction::MSTORE);
|
|
||||||
|
|
||||||
// Insert sub with variable access, but new state
|
|
||||||
CompilerState ns = _s;
|
|
||||||
c = 0;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
if (c++ == 3)
|
|
||||||
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
|
|
||||||
|
|
||||||
// Remove variable
|
|
||||||
auto it = _s.vars.find(key);
|
|
||||||
if (it != _s.vars.end())
|
|
||||||
_s.vars.erase(it);
|
|
||||||
}
|
|
||||||
else if (us == "REF")
|
|
||||||
m_asm.append((u256)varAddress(firstAsString()));
|
|
||||||
else if (us == "DEF")
|
|
||||||
{
|
|
||||||
string n;
|
|
||||||
unsigned ii = 0;
|
|
||||||
if (_t.size() != 3 && _t.size() != 4)
|
|
||||||
error<IncorrectParameterCount>(us);
|
|
||||||
vector<string> args;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
{
|
|
||||||
if (ii == 1)
|
|
||||||
{
|
|
||||||
if (i.tag())
|
|
||||||
error<InvalidName>(toString(i));
|
|
||||||
if (i.which() == sp::utree_type::string_type)
|
|
||||||
{
|
|
||||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
|
||||||
n = string(sr.begin(), sr.end());
|
|
||||||
}
|
|
||||||
else if (i.which() == sp::utree_type::symbol_type)
|
|
||||||
{
|
|
||||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
|
||||||
n = _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ii == 2)
|
|
||||||
if (_t.size() == 3)
|
|
||||||
{
|
|
||||||
/// NOTE: some compilers could do the assignment first if this is done in a single line
|
|
||||||
CodeFragment code = CodeFragment(i, _s, m_readFile);
|
|
||||||
_s.defs[n] = code;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for (auto const& j: i)
|
|
||||||
{
|
|
||||||
if (j.tag() || j.which() != sp::utree_type::symbol_type)
|
|
||||||
error<InvalidMacroArgs>();
|
|
||||||
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
|
|
||||||
args.emplace_back(sr.begin(), sr.end());
|
|
||||||
}
|
|
||||||
else if (ii == 3)
|
|
||||||
{
|
|
||||||
auto k = make_pair(n, args.size());
|
|
||||||
_s.macros[k].code = i;
|
|
||||||
_s.macros[k].env = _s.outers;
|
|
||||||
_s.macros[k].args = args;
|
|
||||||
for (auto const& i: _s.args)
|
|
||||||
_s.macros[k].env[i.first] = i.second;
|
|
||||||
for (auto const& i: _s.defs)
|
|
||||||
_s.macros[k].env[i.first] = i.second;
|
|
||||||
}
|
|
||||||
++ii;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (us == "LIT")
|
|
||||||
{
|
|
||||||
if (_t.size() < 3)
|
|
||||||
error<IncorrectParameterCount>(us);
|
|
||||||
unsigned ii = 0;
|
|
||||||
CodeFragment pos;
|
|
||||||
bytes data;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
{
|
|
||||||
if (ii == 0)
|
|
||||||
{
|
|
||||||
ii++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (ii == 1)
|
|
||||||
{
|
|
||||||
pos = CodeFragment(i, _s, m_readFile);
|
|
||||||
if (pos.m_asm.deposit() != 1)
|
|
||||||
error<InvalidDeposit>(toString(i));
|
|
||||||
}
|
|
||||||
else if (i.tag() != 0)
|
|
||||||
{
|
|
||||||
error<InvalidLiteral>(toString(i));
|
|
||||||
}
|
|
||||||
else if (i.which() == sp::utree_type::string_type)
|
|
||||||
{
|
|
||||||
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
|
|
||||||
data.insert(data.end(), (uint8_t const *)sr.begin(), (uint8_t const*)sr.end());
|
|
||||||
}
|
|
||||||
else if (i.which() == sp::utree_type::any_type)
|
|
||||||
{
|
|
||||||
bigint bi = *i.get<bigint*>();
|
|
||||||
if (bi < 0)
|
|
||||||
error<IntegerOutOfRange>(toString(i));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bytes tmp = toCompactBigEndian(bi);
|
|
||||||
data.insert(data.end(), tmp.begin(), tmp.end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error<InvalidLiteral>(toString(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
ii++;
|
|
||||||
}
|
|
||||||
m_asm.append((u256)data.size());
|
|
||||||
m_asm.append(Instruction::DUP1);
|
|
||||||
m_asm.append(data);
|
|
||||||
m_asm.append(pos.m_asm, 1);
|
|
||||||
m_asm.append(Instruction::CODECOPY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nonStandard = false;
|
|
||||||
|
|
||||||
if (nonStandard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::map<std::string, Instruction> const c_arith = {
|
|
||||||
{ "+", Instruction::ADD },
|
|
||||||
{ "-", Instruction::SUB },
|
|
||||||
{ "*", Instruction::MUL },
|
|
||||||
{ "/", Instruction::DIV },
|
|
||||||
{ "%", Instruction::MOD },
|
|
||||||
{ "&", Instruction::AND },
|
|
||||||
{ "|", Instruction::OR },
|
|
||||||
{ "^", Instruction::XOR }
|
|
||||||
};
|
|
||||||
std::map<std::string, pair<Instruction, bool>> const c_binary = {
|
|
||||||
{ "<", { Instruction::LT, false } },
|
|
||||||
{ "<=", { Instruction::GT, true } },
|
|
||||||
{ ">", { Instruction::GT, false } },
|
|
||||||
{ ">=", { Instruction::LT, true } },
|
|
||||||
{ "S<", { Instruction::SLT, false } },
|
|
||||||
{ "S<=", { Instruction::SGT, true } },
|
|
||||||
{ "S>", { Instruction::SGT, false } },
|
|
||||||
{ "S>=", { Instruction::SLT, true } },
|
|
||||||
{ "=", { Instruction::EQ, false } },
|
|
||||||
{ "!=", { Instruction::EQ, true } }
|
|
||||||
};
|
|
||||||
std::map<std::string, Instruction> const c_unary = {
|
|
||||||
{ "!", Instruction::ISZERO },
|
|
||||||
{ "~", Instruction::NOT }
|
|
||||||
};
|
|
||||||
|
|
||||||
vector<CodeFragment> code;
|
|
||||||
CompilerState ns = _s;
|
|
||||||
ns.vars.clear();
|
|
||||||
ns.usedAlloc = false;
|
|
||||||
int c = _t.tag() ? 1 : 0;
|
|
||||||
for (auto const& i: _t)
|
|
||||||
if (c++)
|
|
||||||
{
|
|
||||||
if (us == "LLL" && c == 1)
|
|
||||||
code.emplace_back(i, ns, m_readFile);
|
|
||||||
else
|
|
||||||
code.emplace_back(i, _s, m_readFile);
|
|
||||||
}
|
|
||||||
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
|
|
||||||
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
|
|
||||||
auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error<IncorrectParameterCount>(us); };
|
|
||||||
auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_asm.deposit() != s) error<InvalidDeposit>(us); };
|
|
||||||
|
|
||||||
if (_s.macros.count(make_pair(s, code.size())))
|
|
||||||
{
|
|
||||||
Macro const& m = _s.macros.at(make_pair(s, code.size()));
|
|
||||||
CompilerState cs = _s;
|
|
||||||
for (auto const& i: m.env)
|
|
||||||
cs.outers[i.first] = i.second;
|
|
||||||
for (auto const& i: cs.defs)
|
|
||||||
cs.outers[i.first] = i.second;
|
|
||||||
cs.defs.clear();
|
|
||||||
for (unsigned i = 0; i < m.args.size(); ++i)
|
|
||||||
{
|
|
||||||
//requireDeposit(i, 1);
|
|
||||||
cs.args[m.args[i]] = code[i];
|
|
||||||
}
|
|
||||||
m_asm.append(CodeFragment(m.code, cs, m_readFile).m_asm);
|
|
||||||
for (auto const& i: cs.defs)
|
|
||||||
_s.defs[i.first] = i.second;
|
|
||||||
for (auto const& i: cs.macros)
|
|
||||||
_s.macros.insert(i);
|
|
||||||
}
|
|
||||||
else if (c_instructions.count(us) && validFunctionalInstruction(us))
|
|
||||||
{
|
|
||||||
auto it = c_instructions.find(us);
|
|
||||||
requireSize(instructionInfo(it->second).args);
|
|
||||||
|
|
||||||
for (unsigned i = code.size(); i; --i)
|
|
||||||
m_asm.append(code[i - 1].m_asm, 1);
|
|
||||||
m_asm.append(it->second);
|
|
||||||
}
|
|
||||||
else if (c_arith.count(us))
|
|
||||||
{
|
|
||||||
auto it = c_arith.find(us);
|
|
||||||
requireMinSize(1);
|
|
||||||
for (unsigned i = code.size(); i; --i)
|
|
||||||
{
|
|
||||||
requireDeposit(i - 1, 1);
|
|
||||||
m_asm.append(code[i - 1].m_asm, 1);
|
|
||||||
}
|
|
||||||
for (unsigned i = 1; i < code.size(); ++i)
|
|
||||||
m_asm.append(it->second);
|
|
||||||
}
|
|
||||||
else if (c_binary.count(us))
|
|
||||||
{
|
|
||||||
auto it = c_binary.find(us);
|
|
||||||
requireSize(2);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
requireDeposit(1, 1);
|
|
||||||
m_asm.append(code[1].m_asm, 1);
|
|
||||||
m_asm.append(code[0].m_asm, 1);
|
|
||||||
m_asm.append(it->second.first);
|
|
||||||
if (it->second.second)
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
}
|
|
||||||
else if (c_unary.count(us))
|
|
||||||
{
|
|
||||||
auto it = c_unary.find(us);
|
|
||||||
requireSize(1);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
m_asm.append(code[0].m_asm, 1);
|
|
||||||
m_asm.append(it->second);
|
|
||||||
}
|
|
||||||
else if (us == "IF")
|
|
||||||
{
|
|
||||||
requireSize(3);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
int minDep = min(code[1].m_asm.deposit(), code[2].m_asm.deposit());
|
|
||||||
|
|
||||||
m_asm.append(code[0].m_asm);
|
|
||||||
auto mainBranch = m_asm.appendJumpI();
|
|
||||||
|
|
||||||
/// The else branch.
|
|
||||||
int startDeposit = m_asm.deposit();
|
|
||||||
m_asm.append(code[2].m_asm, minDep);
|
|
||||||
auto end = m_asm.appendJump();
|
|
||||||
int deposit = m_asm.deposit();
|
|
||||||
m_asm.setDeposit(startDeposit);
|
|
||||||
|
|
||||||
/// The main branch.
|
|
||||||
m_asm << mainBranch.tag();
|
|
||||||
m_asm.append(code[1].m_asm, minDep);
|
|
||||||
m_asm << end.tag();
|
|
||||||
if (m_asm.deposit() != deposit)
|
|
||||||
error<InvalidDeposit>(us);
|
|
||||||
}
|
|
||||||
else if (us == "WHEN" || us == "UNLESS")
|
|
||||||
{
|
|
||||||
requireSize(2);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
|
|
||||||
m_asm.append(code[0].m_asm);
|
|
||||||
if (us == "WHEN")
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
auto end = m_asm.appendJumpI();
|
|
||||||
m_asm.append(code[1].m_asm, 0);
|
|
||||||
m_asm << end.tag();
|
|
||||||
}
|
|
||||||
else if (us == "WHILE" || us == "UNTIL")
|
|
||||||
{
|
|
||||||
requireSize(2);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
|
|
||||||
auto begin = m_asm.append(m_asm.newTag());
|
|
||||||
m_asm.append(code[0].m_asm);
|
|
||||||
if (us == "WHILE")
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
auto end = m_asm.appendJumpI();
|
|
||||||
m_asm.append(code[1].m_asm, 0);
|
|
||||||
m_asm.appendJump(begin);
|
|
||||||
m_asm << end.tag();
|
|
||||||
}
|
|
||||||
else if (us == "FOR")
|
|
||||||
{
|
|
||||||
requireSize(4);
|
|
||||||
requireDeposit(1, 1);
|
|
||||||
|
|
||||||
m_asm.append(code[0].m_asm, 0);
|
|
||||||
auto begin = m_asm.append(m_asm.newTag());
|
|
||||||
m_asm.append(code[1].m_asm);
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
auto end = m_asm.appendJumpI();
|
|
||||||
m_asm.append(code[3].m_asm, 0);
|
|
||||||
m_asm.append(code[2].m_asm, 0);
|
|
||||||
m_asm.appendJump(begin);
|
|
||||||
m_asm << end.tag();
|
|
||||||
}
|
|
||||||
else if (us == "SWITCH")
|
|
||||||
{
|
|
||||||
requireMinSize(1);
|
|
||||||
|
|
||||||
bool hasDefault = (code.size() % 2 == 1);
|
|
||||||
int startDeposit = m_asm.deposit();
|
|
||||||
int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0;
|
|
||||||
|
|
||||||
// The conditions
|
|
||||||
eth::AssemblyItems jumpTags;
|
|
||||||
for (unsigned i = 0; i < code.size() - 1; i += 2)
|
|
||||||
{
|
|
||||||
requireDeposit(i, 1);
|
|
||||||
m_asm.append(code[i].m_asm);
|
|
||||||
jumpTags.push_back(m_asm.appendJumpI());
|
|
||||||
}
|
|
||||||
|
|
||||||
// The default, if present
|
|
||||||
if (hasDefault)
|
|
||||||
m_asm.append(code[code.size() - 1].m_asm);
|
|
||||||
|
|
||||||
// The targets - appending in reverse makes the top case the most efficient.
|
|
||||||
if (code.size() > 1)
|
|
||||||
{
|
|
||||||
auto end = m_asm.appendJump();
|
|
||||||
for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2)
|
|
||||||
{
|
|
||||||
m_asm << jumpTags[i / 2].tag();
|
|
||||||
requireDeposit(i + 1, targetDeposit);
|
|
||||||
m_asm.append(code[i + 1].m_asm);
|
|
||||||
if (i != 0)
|
|
||||||
m_asm.appendJump(end);
|
|
||||||
}
|
|
||||||
m_asm << end.tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_asm.setDeposit(startDeposit + targetDeposit);
|
|
||||||
}
|
|
||||||
else if (us == "ALLOC")
|
|
||||||
{
|
|
||||||
requireSize(1);
|
|
||||||
requireDeposit(0, 1);
|
|
||||||
|
|
||||||
// (alloc N):
|
|
||||||
// - Evaluates to (msize) before the allocation - the start of the allocated memory
|
|
||||||
// - Does not allocate memory when N is zero
|
|
||||||
// - Size of memory allocated is N bytes rounded up to a multiple of 32
|
|
||||||
// - Uses MLOAD to expand MSIZE to avoid modifying memory.
|
|
||||||
|
|
||||||
auto end = m_asm.newTag();
|
|
||||||
m_asm.append(Instruction::MSIZE); // Result will be original top of memory
|
|
||||||
m_asm.append(code[0].m_asm, 1); // The alloc argument N
|
|
||||||
m_asm.append(Instruction::DUP1);
|
|
||||||
m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE
|
|
||||||
m_asm.appendJumpI(end);
|
|
||||||
m_asm.append(u256(1));
|
|
||||||
m_asm.append(Instruction::DUP2); // Copy N
|
|
||||||
m_asm.append(Instruction::SUB); // N-1
|
|
||||||
m_asm.append(u256(0x1f)); // Bit mask
|
|
||||||
m_asm.append(Instruction::NOT); // Invert
|
|
||||||
m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary
|
|
||||||
m_asm.append(Instruction::MSIZE); // MSIZE is cheap
|
|
||||||
m_asm.append(Instruction::ADD);
|
|
||||||
m_asm.append(Instruction::MLOAD); // Updates MSIZE
|
|
||||||
m_asm.append(Instruction::POP); // Discard the result of the MLOAD
|
|
||||||
m_asm.append(end);
|
|
||||||
m_asm.append(Instruction::POP); // Discard duplicate N
|
|
||||||
|
|
||||||
_s.usedAlloc = true;
|
|
||||||
}
|
|
||||||
else if (us == "LLL")
|
|
||||||
{
|
|
||||||
requireMinSize(2);
|
|
||||||
requireMaxSize(3);
|
|
||||||
requireDeposit(1, 1);
|
|
||||||
|
|
||||||
auto subPush = m_asm.appendSubroutine(make_shared<eth::Assembly>(code[0].assembly(ns)));
|
|
||||||
m_asm.append(Instruction::DUP1);
|
|
||||||
if (code.size() == 3)
|
|
||||||
{
|
|
||||||
requireDeposit(2, 1);
|
|
||||||
m_asm.append(code[2].m_asm, 1);
|
|
||||||
m_asm.append(Instruction::LT);
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
m_asm.append(Instruction::MUL);
|
|
||||||
m_asm.append(Instruction::DUP1);
|
|
||||||
}
|
|
||||||
m_asm.append(subPush);
|
|
||||||
m_asm.append(code[1].m_asm, 1);
|
|
||||||
m_asm.append(Instruction::CODECOPY);
|
|
||||||
}
|
|
||||||
else if (us == "&&" || us == "||")
|
|
||||||
{
|
|
||||||
requireMinSize(1);
|
|
||||||
for (unsigned i = 0; i < code.size(); ++i)
|
|
||||||
requireDeposit(i, 1);
|
|
||||||
|
|
||||||
auto end = m_asm.newTag();
|
|
||||||
if (code.size() > 1)
|
|
||||||
{
|
|
||||||
m_asm.append((u256)(us == "||" ? 1 : 0));
|
|
||||||
for (unsigned i = 1; i < code.size(); ++i)
|
|
||||||
{
|
|
||||||
// Check if true - predicate
|
|
||||||
m_asm.append(code[i - 1].m_asm, 1);
|
|
||||||
if (us == "&&")
|
|
||||||
m_asm.append(Instruction::ISZERO);
|
|
||||||
m_asm.appendJumpI(end);
|
|
||||||
}
|
|
||||||
m_asm.append(Instruction::POP);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if true - predicate
|
|
||||||
m_asm.append(code.back().m_asm, 1);
|
|
||||||
|
|
||||||
// At end now.
|
|
||||||
m_asm.append(end);
|
|
||||||
}
|
|
||||||
else if (us == "SEQ")
|
|
||||||
{
|
|
||||||
unsigned ii = 0;
|
|
||||||
for (auto const& i: code)
|
|
||||||
if (++ii < code.size())
|
|
||||||
m_asm.append(i.m_asm, 0);
|
|
||||||
else
|
|
||||||
m_asm.append(i.m_asm);
|
|
||||||
}
|
|
||||||
else if (us == "RAW")
|
|
||||||
{
|
|
||||||
for (auto const& i: code)
|
|
||||||
m_asm.append(i.m_asm);
|
|
||||||
// Leave only the last item on stack.
|
|
||||||
while (m_asm.deposit() > 1)
|
|
||||||
m_asm.append(Instruction::POP);
|
|
||||||
}
|
|
||||||
else if (us == "BYTECODESIZE")
|
|
||||||
{
|
|
||||||
m_asm.appendProgramSize();
|
|
||||||
}
|
|
||||||
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
|
|
||||||
m_asm.append((u256)varAddress(s));
|
|
||||||
else
|
|
||||||
error<InvalidOperation>("Unsupported keyword: '" + us + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeFragment CodeFragment::compile(string _src, CompilerState& _s, ReadCallback const& _readFile)
|
|
||||||
{
|
|
||||||
CodeFragment ret;
|
|
||||||
sp::utree o;
|
|
||||||
parseTreeLLL(std::move(_src), o);
|
|
||||||
if (!o.empty())
|
|
||||||
ret = CodeFragment(o, _s, _readFile);
|
|
||||||
_s.treesToKill.push_back(o);
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file CodeFragment.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <liblll/Exceptions.h>
|
|
||||||
#include <libevmasm/Instruction.h>
|
|
||||||
#include <libevmasm/Assembly.h>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
|
|
||||||
namespace boost { namespace spirit { class utree; } }
|
|
||||||
namespace sp = boost::spirit;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace lll
|
|
||||||
{
|
|
||||||
|
|
||||||
struct CompilerState;
|
|
||||||
|
|
||||||
class CodeFragment
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using ReadCallback = std::function<std::string(std::string const&)>;
|
|
||||||
|
|
||||||
CodeFragment() = default;
|
|
||||||
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
|
|
||||||
|
|
||||||
static CodeFragment compile(std::string _src, CompilerState& _s, ReadCallback const& _readFile);
|
|
||||||
|
|
||||||
/// Consolidates data and compiles code.
|
|
||||||
eth::Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void finalise(CompilerState const& _cs);
|
|
||||||
|
|
||||||
template <class T> static void error() { BOOST_THROW_EXCEPTION(T() ); }
|
|
||||||
template <class T> static void error(std::string const& reason) {
|
|
||||||
auto err = T();
|
|
||||||
err << errinfo_comment(reason);
|
|
||||||
BOOST_THROW_EXCEPTION(err);
|
|
||||||
}
|
|
||||||
void constructOperation(sp::utree const& _t, CompilerState& _s);
|
|
||||||
|
|
||||||
bool m_finalised = false;
|
|
||||||
eth::Assembly m_asm;
|
|
||||||
ReadCallback m_readFile;
|
|
||||||
};
|
|
||||||
|
|
||||||
static CodeFragment const NullCodeFragment;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file Compiler.cpp
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <liblll/Compiler.h>
|
|
||||||
#include <liblll/Parser.h>
|
|
||||||
#include <liblll/CompilerState.h>
|
|
||||||
#include <liblll/CodeFragment.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace dev;
|
|
||||||
using namespace dev::lll;
|
|
||||||
|
|
||||||
bytes dev::lll::compileLLL(string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CompilerState cs;
|
|
||||||
cs.populateStandard();
|
|
||||||
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
|
|
||||||
if (_opt)
|
|
||||||
assembly = assembly.optimise(true, _evmVersion, true, 200);
|
|
||||||
bytes ret = assembly.assemble().bytecode;
|
|
||||||
for (auto i: cs.treesToKill)
|
|
||||||
killBigints(i);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception const& _e)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
{
|
|
||||||
_errors->emplace_back("Parse error.");
|
|
||||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
{
|
|
||||||
_errors->emplace_back("Parse exception.");
|
|
||||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
_errors->emplace_back("Internal compiler exception.");
|
|
||||||
}
|
|
||||||
return bytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string dev::lll::compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CompilerState cs;
|
|
||||||
cs.populateStandard();
|
|
||||||
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
|
|
||||||
if (_opt)
|
|
||||||
assembly = assembly.optimise(true, _evmVersion, true, 200);
|
|
||||||
string ret = assembly.assemblyString();
|
|
||||||
for (auto i: cs.treesToKill)
|
|
||||||
killBigints(i);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch (Exception const& _e)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
{
|
|
||||||
_errors->emplace_back("Parse error.");
|
|
||||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception const& _e)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
{
|
|
||||||
_errors->emplace_back("Parse exception.");
|
|
||||||
_errors->emplace_back(boost::diagnostic_information(_e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
if (_errors)
|
|
||||||
_errors->emplace_back("Internal compiler exception.");
|
|
||||||
}
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
string dev::lll::parseLLL(string _src)
|
|
||||||
{
|
|
||||||
sp::utree o;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
parseTreeLLL(std::move(_src), o);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
killBigints(o);
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
ostringstream ret;
|
|
||||||
debugOutAST(ret, o);
|
|
||||||
killBigints(o);
|
|
||||||
return ret.str();
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file Compiler.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace lll
|
|
||||||
{
|
|
||||||
|
|
||||||
using ReadCallback = std::function<std::string(std::string const&)>;
|
|
||||||
|
|
||||||
std::string parseLLL(std::string _src);
|
|
||||||
std::string compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
|
|
||||||
bytes compileLLL(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file CompilerState.cpp
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <liblll/CompilerState.h>
|
|
||||||
#include <liblll/CodeFragment.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace dev;
|
|
||||||
using namespace dev::lll;
|
|
||||||
|
|
||||||
CompilerState::CompilerState()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeFragment const& CompilerState::getDef(std::string const& _s) const
|
|
||||||
{
|
|
||||||
if (defs.count(_s))
|
|
||||||
return defs.at(_s);
|
|
||||||
else if (args.count(_s))
|
|
||||||
return args.at(_s);
|
|
||||||
else if (outers.count(_s))
|
|
||||||
return outers.at(_s);
|
|
||||||
else
|
|
||||||
return NullCodeFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerState::populateStandard()
|
|
||||||
{
|
|
||||||
static string const s = "{"
|
|
||||||
"(def 'panic () (asm INVALID))"
|
|
||||||
// Alternative macro version of alloc, which is currently implemented in the parser
|
|
||||||
// "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))"
|
|
||||||
"(def 'allgas (- (gas) 21))"
|
|
||||||
"(def 'send (to value) (call allgas to value 0 0 0 0))"
|
|
||||||
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
|
|
||||||
// NOTE: in this macro, memory location 0 is set in order to force msize to be at least 32 bytes.
|
|
||||||
"(def 'msg (gaslimit to value data datasize outsize) { [0]:0 [0]:(msize) (call gaslimit to value data datasize @0 outsize) @0 })"
|
|
||||||
"(def 'msg (gaslimit to value data datasize) { (call gaslimit to value data datasize 0 32) @0 })"
|
|
||||||
"(def 'msg (gaslimit to value data) { [0]:data (msg gaslimit to value 0 32) })"
|
|
||||||
"(def 'msg (to value data) { [0]:data (msg allgas to value 0 32) })"
|
|
||||||
"(def 'msg (to data) { [0]:data (msg allgas to 0 0 32) })"
|
|
||||||
// NOTE: in the create macros, memory location 0 is set in order to force msize to be at least 32 bytes.
|
|
||||||
"(def 'create (value code) { [0]:0 [0]:(msize) (create value @0 (lll code @0)) })"
|
|
||||||
"(def 'create (code) { [0]:0 [0]:(msize) (create 0 @0 (lll code @0)) })"
|
|
||||||
"(def 'sha3 (loc len) (keccak256 loc len))"
|
|
||||||
"(def 'sha3 (val) { [0]:val (sha3 0 32) })"
|
|
||||||
"(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })"
|
|
||||||
"(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })"
|
|
||||||
"(def 'return (val) { [0]:val (return 0 32) })"
|
|
||||||
"(def 'returnlll (code) (return 0 (lll code 0)) )"
|
|
||||||
"(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )"
|
|
||||||
"(def 'permcount 0)"
|
|
||||||
"(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )"
|
|
||||||
"(def 'ecrecover (hash v r s) { [0] hash [32] v [64] r [96] s (msg allgas 1 0 0 128) })"
|
|
||||||
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
|
|
||||||
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
|
|
||||||
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
|
|
||||||
"(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })"
|
|
||||||
"(def 'wei 1)"
|
|
||||||
"(def 'szabo 1000000000000)"
|
|
||||||
"(def 'finney 1000000000000000)"
|
|
||||||
"(def 'ether 1000000000000000000)"
|
|
||||||
// these could be replaced by native instructions once supported by EVM
|
|
||||||
"(def 'shl (val shift) (mul val (exp 2 shift)))"
|
|
||||||
"(def 'shr (val shift) (div val (exp 2 shift)))"
|
|
||||||
"}";
|
|
||||||
CodeFragment::compile(s, *this, CodeFragment::ReadCallback());
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file CompilerState.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <liblll/CodeFragment.h>
|
|
||||||
#include <boost/spirit/include/support_utree.hpp>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace lll
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Macro
|
|
||||||
{
|
|
||||||
std::vector<std::string> args;
|
|
||||||
boost::spirit::utree code;
|
|
||||||
std::map<std::string, CodeFragment> env;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompilerState
|
|
||||||
{
|
|
||||||
CompilerState();
|
|
||||||
|
|
||||||
CodeFragment const& getDef(std::string const& _s) const;
|
|
||||||
void populateStandard();
|
|
||||||
|
|
||||||
unsigned stackSize = 128;
|
|
||||||
std::map<std::string, std::pair<unsigned, unsigned>> vars; ///< maps name to stack offset & size.
|
|
||||||
std::map<std::string, CodeFragment> defs;
|
|
||||||
std::map<std::string, CodeFragment> args;
|
|
||||||
std::map<std::string, CodeFragment> outers;
|
|
||||||
std::map<std::pair<std::string, unsigned>, Macro> macros;
|
|
||||||
std::vector<boost::spirit::utree> treesToKill;
|
|
||||||
bool usedAlloc = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file Exceptions.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <libdevcore/Exceptions.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace lll
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Compile a Low-level Lisp-like Language program into EVM-code.
|
|
||||||
class CompilerException: public dev::Exception {};
|
|
||||||
class InvalidOperation: public CompilerException {};
|
|
||||||
class IntegerOutOfRange: public CompilerException {};
|
|
||||||
class EmptyList: public CompilerException {};
|
|
||||||
class DataNotExecutable: public CompilerException {};
|
|
||||||
class IncorrectParameterCount: public CompilerException {};
|
|
||||||
class InvalidName: public CompilerException {};
|
|
||||||
class InvalidMacroArgs: public CompilerException {};
|
|
||||||
class InvalidLiteral: public CompilerException {};
|
|
||||||
class BareSymbol: public CompilerException {};
|
|
||||||
class ParserException: public CompilerException {};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file Parser.cpp
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <liblll/Parser.h>
|
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(disable:4348)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BOOST_RESULT_OF_USE_DECLTYPE
|
|
||||||
#define BOOST_SPIRIT_USE_PHOENIX_V3
|
|
||||||
#include <boost/spirit/include/qi.hpp>
|
|
||||||
#include <boost/spirit/include/phoenix.hpp>
|
|
||||||
#include <boost/spirit/include/support_utree.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace dev;
|
|
||||||
using namespace dev::lll;
|
|
||||||
namespace qi = boost::spirit::qi;
|
|
||||||
namespace px = boost::phoenix;
|
|
||||||
namespace sp = boost::spirit;
|
|
||||||
|
|
||||||
void dev::lll::killBigints(sp::utree const& _this)
|
|
||||||
{
|
|
||||||
switch (_this.which())
|
|
||||||
{
|
|
||||||
case sp::utree_type::list_type: for (auto const& i: _this) killBigints(i); break;
|
|
||||||
case sp::utree_type::any_type: delete _this.get<bigint*>(); break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dev::lll::debugOutAST(ostream& _out, sp::utree const& _this)
|
|
||||||
{
|
|
||||||
switch (_this.which())
|
|
||||||
{
|
|
||||||
case sp::utree_type::list_type:
|
|
||||||
switch (_this.tag())
|
|
||||||
{
|
|
||||||
case 0: _out << "( "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << ")"; break;
|
|
||||||
case 1: _out << "@ "; debugOutAST(_out, _this.front()); break;
|
|
||||||
case 2: _out << "@@ "; debugOutAST(_out, _this.front()); break;
|
|
||||||
case 3: _out << "[ "; debugOutAST(_out, _this.front()); _out << " ] "; debugOutAST(_out, _this.back()); break;
|
|
||||||
case 4: _out << "[[ "; debugOutAST(_out, _this.front()); _out << " ]] "; debugOutAST(_out, _this.back()); break;
|
|
||||||
case 5: _out << "{ "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << "}"; break;
|
|
||||||
case 6: _out << "$ "; debugOutAST(_out, _this.front()); break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case sp::utree_type::int_type: _out << _this.get<int>(); break;
|
|
||||||
case sp::utree_type::string_type: _out << "\"" << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>() << "\""; break;
|
|
||||||
case sp::utree_type::symbol_type: _out << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>(); break;
|
|
||||||
case sp::utree_type::any_type: _out << *_this.get<bigint*>(); break;
|
|
||||||
default: _out << "nil";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace dev {
|
|
||||||
namespace lll {
|
|
||||||
namespace parseTreeLLL_ {
|
|
||||||
|
|
||||||
template<unsigned N>
|
|
||||||
struct tagNode
|
|
||||||
{
|
|
||||||
void operator()(sp::utree& n, qi::rule<string::const_iterator, qi::ascii::space_type, sp::utree()>::context_type& c) const
|
|
||||||
{
|
|
||||||
(boost::fusion::at_c<0>(c.attributes) = n).tag(N);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}}}
|
|
||||||
|
|
||||||
void dev::lll::parseTreeLLL(string const& _s, sp::utree& o_out)
|
|
||||||
{
|
|
||||||
using qi::standard::space;
|
|
||||||
using qi::standard::space_type;
|
|
||||||
using dev::lll::parseTreeLLL_::tagNode;
|
|
||||||
using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>;
|
|
||||||
using it = string::const_iterator;
|
|
||||||
|
|
||||||
qi::rule<it, space_type, sp::utree()> element;
|
|
||||||
qi::rule<it, string()> str = '"' > qi::lexeme[+(~qi::char_(std::string("\"") + '\0'))] > '"';
|
|
||||||
qi::rule<it, string()> strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\0'))];
|
|
||||||
qi::rule<it, symbol_type()> symbol = qi::lexeme[+(~qi::char_(std::string(" $@[]{}:();\"\x01-\x1f\x7f") + '\0'))];
|
|
||||||
qi::rule<it, string()> intstr = qi::lexeme[ qi::no_case["0x"][qi::_val = "0x"] >> +qi::char_("0-9a-fA-F")[qi::_val += qi::_1]] | qi::lexeme[+qi::char_("0-9")[qi::_val += qi::_1]];
|
|
||||||
qi::rule<it, sp::utree()> integer = intstr[qi::_val = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))];
|
|
||||||
qi::rule<it, space_type, sp::utree()> atom = integer[qi::_val = qi::_1] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1];
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> seq = '{' > *element > '}';
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> mload = '@' > element;
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> sload = qi::lit("@@") > element;
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> mstore = '[' > element > ']' > -qi::lit(":") > element;
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element;
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> calldataload = qi::lit("$") > element;
|
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> list = '(' > *element > ')';
|
|
||||||
|
|
||||||
qi::rule<it, space_type, sp::utree()> extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()];
|
|
||||||
element = atom | list | extra;
|
|
||||||
|
|
||||||
string s;
|
|
||||||
s.reserve(_s.size());
|
|
||||||
bool incomment = false;
|
|
||||||
bool instring = false;
|
|
||||||
bool insstring = false;
|
|
||||||
for (auto i: _s)
|
|
||||||
{
|
|
||||||
if (i == ';' && !instring && !insstring)
|
|
||||||
incomment = true;
|
|
||||||
else if (i == '\n')
|
|
||||||
incomment = instring = insstring = false;
|
|
||||||
else if (i == '"' && !insstring)
|
|
||||||
instring = !instring;
|
|
||||||
else if (i == '\'')
|
|
||||||
insstring = true;
|
|
||||||
else if (i == ' ')
|
|
||||||
insstring = false;
|
|
||||||
if (!incomment)
|
|
||||||
s.push_back(i);
|
|
||||||
}
|
|
||||||
auto ret = s.cbegin();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
qi::phrase_parse(ret, s.cend(), element, space, qi::skip_flag::dont_postskip, o_out);
|
|
||||||
}
|
|
||||||
catch (qi::expectation_failure<it> const& e)
|
|
||||||
{
|
|
||||||
std::string fragment(e.first, e.last);
|
|
||||||
std::string loc = to_string(std::distance(s.cbegin(), e.first) - 1);
|
|
||||||
std::string reason("Lexer failure at " + loc + ": '" + fragment + "'");
|
|
||||||
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment(reason));
|
|
||||||
}
|
|
||||||
for (auto i = ret; i != s.cend(); ++i)
|
|
||||||
if (!isspace(*i))
|
|
||||||
{
|
|
||||||
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
/** @file Parser.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <liblll/Exceptions.h>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace boost { namespace spirit { class utree; } }
|
|
||||||
namespace sp = boost::spirit;
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace lll
|
|
||||||
{
|
|
||||||
|
|
||||||
void killBigints(sp::utree const& _this);
|
|
||||||
void parseTreeLLL(std::string const& _s, sp::utree& o_out);
|
|
||||||
void debugOutAST(std::ostream& _out, sp::utree const& _this);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,8 +24,8 @@
|
|||||||
#include <libsolidity/interface/StandardCompiler.h>
|
#include <libsolidity/interface/StandardCompiler.h>
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libyul/YulString.h>
|
#include <libyul/YulString.h>
|
||||||
#include <libdevcore/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <libdevcore/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -34,8 +34,11 @@
|
|||||||
#include "license.h"
|
#include "license.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
using solidity::frontend::ReadCallback;
|
||||||
|
using solidity::frontend::StandardCompiler;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -122,7 +125,7 @@ extern char const* solidity_license() noexcept
|
|||||||
|
|
||||||
extern char const* solidity_version() noexcept
|
extern char const* solidity_version() noexcept
|
||||||
{
|
{
|
||||||
return VersionString.c_str();
|
return frontend::VersionString.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback, void* _readContext) noexcept
|
extern char* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback, void* _readContext) noexcept
|
||||||
|
@ -39,10 +39,14 @@ set(sources
|
|||||||
ast/ASTAnnotations.h
|
ast/ASTAnnotations.h
|
||||||
ast/ASTEnums.h
|
ast/ASTEnums.h
|
||||||
ast/ASTForward.h
|
ast/ASTForward.h
|
||||||
|
ast/AsmJsonImporter.cpp
|
||||||
|
ast/AsmJsonImporter.h
|
||||||
ast/ASTJsonConverter.cpp
|
ast/ASTJsonConverter.cpp
|
||||||
ast/ASTJsonConverter.h
|
ast/ASTJsonConverter.h
|
||||||
ast/ASTUtils.cpp
|
ast/ASTUtils.cpp
|
||||||
ast/ASTUtils.h
|
ast/ASTUtils.h
|
||||||
|
ast/ASTJsonImporter.cpp
|
||||||
|
ast/ASTJsonImporter.h
|
||||||
ast/ASTVisitor.h
|
ast/ASTVisitor.h
|
||||||
ast/ExperimentalFeatures.h
|
ast/ExperimentalFeatures.h
|
||||||
ast/Types.cpp
|
ast/Types.cpp
|
||||||
@ -151,7 +155,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||||
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore Boost::boost Boost::filesystem Boost::system)
|
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
|
||||||
|
|
||||||
if (${Z3_FOUND})
|
if (${Z3_FOUND})
|
||||||
target_link_libraries(solidity PUBLIC z3::libz3)
|
target_link_libraries(solidity PUBLIC z3::libz3)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user